Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

FeedPeriodsStep.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. * SonarQube, open source software quality management tool.
  3. * Copyright (C) 2008-2014 SonarSource
  4. * mailto:contact AT sonarsource DOT com
  5. *
  6. * SonarQube is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * SonarQube is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.computation.step;
  21. import com.google.common.base.Strings;
  22. import java.util.ArrayList;
  23. import java.util.Calendar;
  24. import java.util.Collections;
  25. import java.util.Date;
  26. import java.util.List;
  27. import javax.annotation.CheckForNull;
  28. import javax.annotation.Nullable;
  29. import org.apache.commons.lang.StringUtils;
  30. import org.sonar.api.CoreProperties;
  31. import org.sonar.api.config.Settings;
  32. import org.sonar.api.resources.Qualifiers;
  33. import org.sonar.api.utils.DateUtils;
  34. import org.sonar.api.utils.log.Logger;
  35. import org.sonar.api.utils.log.Loggers;
  36. import org.sonar.db.component.ComponentDto;
  37. import org.sonar.db.component.SnapshotDto;
  38. import org.sonar.db.component.SnapshotQuery;
  39. import org.sonar.db.DbSession;
  40. import org.sonar.server.computation.batch.BatchReportReader;
  41. import org.sonar.server.computation.component.Component;
  42. import org.sonar.server.computation.component.TreeRootHolder;
  43. import org.sonar.server.computation.period.Period;
  44. import org.sonar.server.computation.period.PeriodsHolderImpl;
  45. import org.sonar.server.db.DbClient;
  46. import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE;
  47. import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.ASC;
  48. import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.DESC;
  49. /**
  50. * Populates the {@link org.sonar.server.computation.period.PeriodsHolder}
  51. *
  52. * Here is how these periods are computed :
  53. * - Read the 5 period properties ${@link CoreProperties#TIMEMACHINE_PERIOD_PREFIX}
  54. * - Try to find the matching snapshots from the properties
  55. * - If a snapshot is found, a new period is added to the repository
  56. */
  57. public class FeedPeriodsStep implements ComputationStep {
  58. private static final Logger LOG = Loggers.get(FeedPeriodsStep.class);
  59. private static final int NUMBER_OF_PERIODS = 5;
  60. private final DbClient dbClient;
  61. private final Settings settings;
  62. private final TreeRootHolder treeRootHolder;
  63. private final BatchReportReader batchReportReader;
  64. private final PeriodsHolderImpl periodsHolder;
  65. public FeedPeriodsStep(DbClient dbClient, Settings settings, TreeRootHolder treeRootHolder, BatchReportReader batchReportReader,
  66. PeriodsHolderImpl periodsHolder) {
  67. this.dbClient = dbClient;
  68. this.settings = settings;
  69. this.treeRootHolder = treeRootHolder;
  70. this.batchReportReader = batchReportReader;
  71. this.periodsHolder = periodsHolder;
  72. }
  73. @Override
  74. public void execute() {
  75. DbSession session = dbClient.openSession(false);
  76. try {
  77. periodsHolder.setPeriods(buildPeriods(session));
  78. } finally {
  79. session.close();
  80. }
  81. }
  82. private List<Period> buildPeriods(DbSession session) {
  83. Component project = treeRootHolder.getRoot();
  84. ComponentDto projectDto = dbClient.componentDao().selectNullableByKey(session, project.getKey());
  85. // No project on first analysis, no period
  86. if (projectDto != null) {
  87. List<Period> periods = new ArrayList<>(5);
  88. PeriodResolver periodResolver = new PeriodResolver(session, projectDto.getId(), batchReportReader.readMetadata().getAnalysisDate(), project.getVersion(),
  89. // TODO qualifier will be different for Views
  90. Qualifiers.PROJECT);
  91. for (int index = 1; index <= NUMBER_OF_PERIODS; index++) {
  92. Period period = periodResolver.resolve(index);
  93. // SONAR-4700 Add a past snapshot only if it exists
  94. if (period != null) {
  95. periods.add(period);
  96. }
  97. }
  98. return periods;
  99. }
  100. return Collections.emptyList();
  101. }
  102. private class PeriodResolver {
  103. private final DbSession session;
  104. private final long projectId;
  105. private final long analysisDate;
  106. private final String currentVersion;
  107. private final String qualifier;
  108. public PeriodResolver(DbSession session, long projectId, long analysisDate, String currentVersion, String qualifier) {
  109. this.session = session;
  110. this.projectId = projectId;
  111. this.analysisDate = analysisDate;
  112. this.currentVersion = currentVersion;
  113. this.qualifier = qualifier;
  114. }
  115. @CheckForNull
  116. public Period resolve(int index) {
  117. String propertyValue = getPropertyValue(qualifier, settings, index);
  118. if (StringUtils.isBlank(propertyValue)) {
  119. return null;
  120. }
  121. Period period = resolve(index, propertyValue);
  122. if (period == null && StringUtils.isNotBlank(propertyValue)) {
  123. LOG.debug("Property " + CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index + " is not valid: " + propertyValue);
  124. }
  125. return period;
  126. }
  127. @CheckForNull
  128. private Period resolve(int index, String property) {
  129. Integer days = tryToResolveByDays(property);
  130. if (days != null) {
  131. return findByDays(index, days);
  132. }
  133. Date date = tryToResolveByDate(property);
  134. if (date != null) {
  135. return findByDate(index, date);
  136. }
  137. if (StringUtils.equals(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, property)) {
  138. return findByPreviousAnalysis(index);
  139. }
  140. if (StringUtils.equals(CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, property)) {
  141. return findByPreviousVersion(index);
  142. }
  143. return findByVersion(index, property);
  144. }
  145. private Period findByDate(int index, Date date) {
  146. SnapshotDto snapshot = findFirstSnapshot(session, createCommonQuery(projectId).setCreatedAfter(date.getTime()).setSort(BY_DATE, ASC));
  147. if (snapshot == null) {
  148. return null;
  149. }
  150. LOG.debug("Compare to date {} (analysis of {})", formatDate(date.getTime()), formatDate(snapshot.getCreatedAt()));
  151. return new Period(index, CoreProperties.TIMEMACHINE_MODE_DATE, DateUtils.formatDate(date), snapshot.getCreatedAt(), snapshot.getId());
  152. }
  153. @CheckForNull
  154. private Period findByDays(int index, int days) {
  155. List<SnapshotDto> snapshots = dbClient.snapshotDao().selectSnapshotsByQuery(session, createCommonQuery(projectId).setCreatedBefore(analysisDate).setSort(BY_DATE, ASC));
  156. long targetDate = DateUtils.addDays(new Date(analysisDate), -days).getTime();
  157. SnapshotDto snapshot = findNearestSnapshotToTargetDate(snapshots, targetDate);
  158. if (snapshot == null) {
  159. return null;
  160. }
  161. LOG.debug("Compare over {} days ({}, analysis of {})", String.valueOf(days), formatDate(targetDate), formatDate(snapshot.getCreatedAt()));
  162. return new Period(index, CoreProperties.TIMEMACHINE_MODE_DAYS, String.valueOf(days), snapshot.getCreatedAt(), snapshot.getId());
  163. }
  164. @CheckForNull
  165. private Period findByPreviousAnalysis(int index) {
  166. SnapshotDto snapshot = findFirstSnapshot(session, createCommonQuery(projectId).setCreatedBefore(analysisDate).setIsLast(true).setSort(BY_DATE, DESC));
  167. if (snapshot == null) {
  168. return null;
  169. }
  170. LOG.debug("Compare to previous analysis ({})", formatDate(snapshot.getCreatedAt()));
  171. return new Period(index, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_ANALYSIS, formatDate(snapshot.getCreatedAt()), snapshot.getCreatedAt(), snapshot.getId());
  172. }
  173. @CheckForNull
  174. private Period findByPreviousVersion(int index) {
  175. List<SnapshotDto> snapshotDtos = dbClient.snapshotDao().selectPreviousVersionSnapshots(session, projectId, currentVersion);
  176. if (snapshotDtos.isEmpty()) {
  177. return null;
  178. }
  179. SnapshotDto snapshotDto = snapshotDtos.get(0);
  180. LOG.debug("Compare to previous version ({})", formatDate(snapshotDto.getCreatedAt()));
  181. return new Period(index, CoreProperties.TIMEMACHINE_MODE_PREVIOUS_VERSION, snapshotDto.getVersion(), snapshotDto.getCreatedAt(), snapshotDto.getId());
  182. }
  183. @CheckForNull
  184. private Period findByVersion(int index, String version) {
  185. SnapshotDto snapshot = findFirstSnapshot(session, createCommonQuery(projectId).setVersion(version).setSort(BY_DATE, DESC));
  186. if (snapshot == null) {
  187. return null;
  188. }
  189. LOG.debug("Compare to version ({}) ({})", version, formatDate(snapshot.getCreatedAt()));
  190. return new Period(index, CoreProperties.TIMEMACHINE_MODE_VERSION, version, snapshot.getCreatedAt(), snapshot.getId());
  191. }
  192. @CheckForNull
  193. private SnapshotDto findFirstSnapshot(DbSession session, SnapshotQuery query) {
  194. List<SnapshotDto> snapshots = dbClient.snapshotDao().selectSnapshotsByQuery(session, query);
  195. if (!snapshots.isEmpty()) {
  196. return snapshots.get(0);
  197. }
  198. return null;
  199. }
  200. }
  201. @CheckForNull
  202. private static Integer tryToResolveByDays(String property) {
  203. try {
  204. return Integer.parseInt(property);
  205. } catch (NumberFormatException e) {
  206. // Nothing to, it means that the property is not a number of days
  207. return null;
  208. }
  209. }
  210. @CheckForNull
  211. private static Date tryToResolveByDate(String property) {
  212. try {
  213. return DateUtils.parseDate(property);
  214. } catch (Exception e) {
  215. // Nothing to, it means that the property is not a date
  216. return null;
  217. }
  218. }
  219. @CheckForNull
  220. private static SnapshotDto findNearestSnapshotToTargetDate(List<SnapshotDto> snapshots, Long targetDate) {
  221. long bestDistance = Long.MAX_VALUE;
  222. SnapshotDto nearest = null;
  223. for (SnapshotDto snapshot : snapshots) {
  224. long distance = Math.abs(snapshot.getCreatedAt() - targetDate);
  225. if (distance <= bestDistance) {
  226. bestDistance = distance;
  227. nearest = snapshot;
  228. }
  229. }
  230. return nearest;
  231. }
  232. private static SnapshotQuery createCommonQuery(Long projectId) {
  233. return new SnapshotQuery().setComponentId(projectId).setStatus(SnapshotDto.STATUS_PROCESSED);
  234. }
  235. private static String formatDate(long date) {
  236. return DateUtils.formatDate(org.apache.commons.lang.time.DateUtils.truncate(new Date(date), Calendar.SECOND));
  237. }
  238. private static String getPropertyValue(@Nullable String qualifier, Settings settings, int index) {
  239. String value = settings.getString(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index);
  240. // For periods 4 and 5 we're also searching for a property prefixed by the qualifier
  241. if (index > 3 && Strings.isNullOrEmpty(value)) {
  242. value = settings.getString(CoreProperties.TIMEMACHINE_PERIOD_PREFIX + index + "." + qualifier);
  243. }
  244. return value;
  245. }
  246. @Override
  247. public String getDescription() {
  248. return "Feed differential periods";
  249. }
  250. }