Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

ProjectMeasuresIndexerIterator.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program 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. * This program 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.db.measure;
  21. import com.google.common.collect.ImmutableMap;
  22. import com.google.common.collect.ImmutableSortedSet;
  23. import java.sql.PreparedStatement;
  24. import java.sql.ResultSet;
  25. import java.sql.SQLException;
  26. import java.util.ArrayList;
  27. import java.util.HashMap;
  28. import java.util.Iterator;
  29. import java.util.LinkedHashMap;
  30. import java.util.List;
  31. import java.util.Map;
  32. import java.util.Optional;
  33. import java.util.Set;
  34. import java.util.concurrent.atomic.AtomicInteger;
  35. import java.util.function.Consumer;
  36. import java.util.stream.Collectors;
  37. import javax.annotation.CheckForNull;
  38. import javax.annotation.Nullable;
  39. import org.apache.commons.lang.StringUtils;
  40. import org.sonar.api.measures.CoreMetrics;
  41. import org.sonar.api.resources.Qualifiers;
  42. import org.sonar.core.util.CloseableIterator;
  43. import org.sonar.db.DatabaseUtils;
  44. import org.sonar.db.DbSession;
  45. import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
  46. import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY;
  47. import static org.sonar.api.utils.KeyValueFormat.parseStringInt;
  48. import static org.sonar.db.component.DbTagsReader.readDbTags;
  49. public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMeasuresIndexerIterator.ProjectMeasures> {
  50. public static final Set<String> METRIC_KEYS = ImmutableSortedSet.of(
  51. CoreMetrics.NCLOC_KEY,
  52. CoreMetrics.LINES_KEY,
  53. CoreMetrics.DUPLICATED_LINES_DENSITY_KEY,
  54. CoreMetrics.COVERAGE_KEY,
  55. CoreMetrics.SQALE_RATING_KEY,
  56. CoreMetrics.RELIABILITY_RATING_KEY,
  57. CoreMetrics.SECURITY_RATING_KEY,
  58. CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_KEY,
  59. CoreMetrics.SECURITY_REVIEW_RATING_KEY,
  60. CoreMetrics.ALERT_STATUS_KEY,
  61. CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION_KEY,
  62. CoreMetrics.NEW_SECURITY_RATING_KEY,
  63. CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY,
  64. CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY,
  65. CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY,
  66. CoreMetrics.NEW_COVERAGE_KEY,
  67. CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY,
  68. CoreMetrics.NEW_LINES_KEY,
  69. CoreMetrics.NEW_RELIABILITY_RATING_KEY);
  70. private static final String SQL_PROJECTS = "SELECT p.uuid, p.kee, p.name, s.created_at, p.tags, p.qualifier " +
  71. "FROM projects p " +
  72. "LEFT OUTER JOIN snapshots s ON s.component_uuid=p.uuid AND s.islast=? " +
  73. "WHERE p.qualifier in (?, ?)";
  74. private static final String PROJECT_FILTER = " AND p.uuid=?";
  75. private static final String SQL_MEASURES = "SELECT m.name, pm.value, pm.variation, pm.text_value FROM live_measures pm " +
  76. "INNER JOIN metrics m ON m.uuid = pm.metric_uuid " +
  77. "WHERE pm.component_uuid = ? " +
  78. "AND m.name IN ({metricNames}) " +
  79. "AND (pm.value IS NOT NULL OR pm.variation IS NOT NULL OR pm.text_value IS NOT NULL) " +
  80. "AND m.enabled = ? ";
  81. private static final boolean ENABLED = true;
  82. private static final int FIELD_METRIC_NAME = 1;
  83. private static final int FIELD_MEASURE_VALUE = 2;
  84. private static final int FIELD_MEASURE_VARIATION = 3;
  85. private static final int FIELD_MEASURE_TEXT_VALUE = 4;
  86. private final PreparedStatement measuresStatement;
  87. private final Iterator<Project> projects;
  88. private ProjectMeasuresIndexerIterator(PreparedStatement measuresStatement, List<Project> projects) {
  89. this.measuresStatement = measuresStatement;
  90. this.projects = projects.iterator();
  91. }
  92. public static ProjectMeasuresIndexerIterator create(DbSession session, @Nullable String projectUuid) {
  93. List<Project> projects = selectProjects(session, projectUuid);
  94. PreparedStatement projectsStatement = createMeasuresStatement(session);
  95. return new ProjectMeasuresIndexerIterator(projectsStatement, projects);
  96. }
  97. private static List<Project> selectProjects(DbSession session, @Nullable String projectUuid) {
  98. List<Project> projects = new ArrayList<>();
  99. try (PreparedStatement stmt = createProjectsStatement(session, projectUuid);
  100. ResultSet rs = stmt.executeQuery()) {
  101. while (rs.next()) {
  102. String uuid = rs.getString(1);
  103. String key = rs.getString(2);
  104. String name = rs.getString(3);
  105. Long analysisDate = DatabaseUtils.getLong(rs, 4);
  106. List<String> tags = readDbTags(DatabaseUtils.getString(rs, 5));
  107. String qualifier = rs.getString(6);
  108. Project project = new Project(uuid, key, name, qualifier, tags, analysisDate);
  109. projects.add(project);
  110. }
  111. return projects;
  112. } catch (SQLException e) {
  113. throw new IllegalStateException("Fail to execute request to select all projects", e);
  114. }
  115. }
  116. private static PreparedStatement createProjectsStatement(DbSession session, @Nullable String projectUuid) {
  117. try {
  118. StringBuilder sql = new StringBuilder(SQL_PROJECTS);
  119. if (projectUuid != null) {
  120. sql.append(PROJECT_FILTER);
  121. }
  122. PreparedStatement stmt = session.getConnection().prepareStatement(sql.toString());
  123. stmt.setBoolean(1, true);
  124. stmt.setString(2, Qualifiers.PROJECT);
  125. stmt.setString(3, Qualifiers.APP);
  126. if (projectUuid != null) {
  127. stmt.setString(4, projectUuid);
  128. }
  129. return stmt;
  130. } catch (SQLException e) {
  131. throw new IllegalStateException("Fail to prepare SQL request to select all project measures", e);
  132. }
  133. }
  134. private static PreparedStatement createMeasuresStatement(DbSession session) {
  135. try {
  136. String metricNameQuestionMarks = METRIC_KEYS.stream().map(x -> "?").collect(Collectors.joining(","));
  137. String sql = StringUtils.replace(SQL_MEASURES, "{metricNames}", metricNameQuestionMarks);
  138. return session.getConnection().prepareStatement(sql);
  139. } catch (SQLException e) {
  140. throw new IllegalStateException("Fail to prepare SQL request to select measures", e);
  141. }
  142. }
  143. @Override
  144. @CheckForNull
  145. protected ProjectMeasures doNext() {
  146. if (!projects.hasNext()) {
  147. return null;
  148. }
  149. Project project = projects.next();
  150. Measures measures = selectMeasures(project.getUuid());
  151. return new ProjectMeasures(project, measures);
  152. }
  153. private Measures selectMeasures(String projectUuid) {
  154. Measures measures = new Measures();
  155. ResultSet rs = null;
  156. try {
  157. AtomicInteger index = new AtomicInteger(1);
  158. measuresStatement.setString(index.getAndIncrement(), projectUuid);
  159. METRIC_KEYS.forEach(DatabaseUtils.setStrings(measuresStatement, index::getAndIncrement));
  160. measuresStatement.setBoolean(index.getAndIncrement(), ENABLED);
  161. rs = measuresStatement.executeQuery();
  162. while (rs.next()) {
  163. readMeasure(rs, measures);
  164. }
  165. return measures;
  166. } catch (Exception e) {
  167. throw new IllegalStateException(String.format("Fail to execute request to select measures of project %s", projectUuid), e);
  168. } finally {
  169. DatabaseUtils.closeQuietly(rs);
  170. }
  171. }
  172. private static void readMeasure(ResultSet rs, Measures measures) throws SQLException {
  173. String metricKey = rs.getString(FIELD_METRIC_NAME);
  174. Optional<Double> value = metricKey.startsWith("new_") ? getDouble(rs, FIELD_MEASURE_VARIATION) : getDouble(rs, FIELD_MEASURE_VALUE);
  175. if (value.isPresent()) {
  176. measures.addNumericMeasure(metricKey, value.get());
  177. return;
  178. }
  179. if (ALERT_STATUS_KEY.equals(metricKey)) {
  180. readTextValue(rs, measures::setQualityGateStatus);
  181. return;
  182. }
  183. if (NCLOC_LANGUAGE_DISTRIBUTION_KEY.equals(metricKey)) {
  184. readTextValue(rs, measures::setNclocByLanguages);
  185. return;
  186. }
  187. }
  188. private static void readTextValue(ResultSet rs, Consumer<String> action) throws SQLException {
  189. String textValue = rs.getString(FIELD_MEASURE_TEXT_VALUE);
  190. if (!rs.wasNull()) {
  191. action.accept(textValue);
  192. }
  193. }
  194. @Override
  195. protected void doClose() throws Exception {
  196. measuresStatement.close();
  197. }
  198. private static Optional<Double> getDouble(ResultSet rs, int index) {
  199. try {
  200. Double value = rs.getDouble(index);
  201. if (!rs.wasNull()) {
  202. return Optional.of(value);
  203. }
  204. return Optional.empty();
  205. } catch (SQLException e) {
  206. throw new IllegalStateException("Fail to get double value", e);
  207. }
  208. }
  209. public static class Project {
  210. private final String uuid;
  211. private final String key;
  212. private final String name;
  213. private final String qualifier;
  214. private final Long analysisDate;
  215. private final List<String> tags;
  216. public Project(String uuid, String key, String name, String qualifier, List<String> tags, @Nullable Long analysisDate) {
  217. this.uuid = uuid;
  218. this.key = key;
  219. this.name = name;
  220. this.qualifier = qualifier;
  221. this.tags = tags;
  222. this.analysisDate = analysisDate;
  223. }
  224. public String getUuid() {
  225. return uuid;
  226. }
  227. public String getKey() {
  228. return key;
  229. }
  230. public String getName() {
  231. return name;
  232. }
  233. public String getQualifier() {
  234. return qualifier;
  235. }
  236. public List<String> getTags() {
  237. return tags;
  238. }
  239. @CheckForNull
  240. public Long getAnalysisDate() {
  241. return analysisDate;
  242. }
  243. }
  244. public static class Measures {
  245. private Map<String, Double> numericMeasures = new HashMap<>();
  246. private String qualityGateStatus;
  247. private Map<String, Integer> nclocByLanguages = new LinkedHashMap<>();
  248. Measures addNumericMeasure(String metricKey, double value) {
  249. numericMeasures.put(metricKey, value);
  250. return this;
  251. }
  252. public Map<String, Double> getNumericMeasures() {
  253. return numericMeasures;
  254. }
  255. Measures setQualityGateStatus(@Nullable String qualityGateStatus) {
  256. this.qualityGateStatus = qualityGateStatus;
  257. return this;
  258. }
  259. @CheckForNull
  260. public String getQualityGateStatus() {
  261. return qualityGateStatus;
  262. }
  263. Measures setNclocByLanguages(String nclocByLangues) {
  264. this.nclocByLanguages = ImmutableMap.copyOf(parseStringInt(nclocByLangues));
  265. return this;
  266. }
  267. public Map<String, Integer> getNclocByLanguages() {
  268. return nclocByLanguages;
  269. }
  270. }
  271. public static class ProjectMeasures {
  272. private Project project;
  273. private Measures measures;
  274. public ProjectMeasures(Project project, Measures measures) {
  275. this.project = project;
  276. this.measures = measures;
  277. }
  278. public Project getProject() {
  279. return project;
  280. }
  281. public Measures getMeasures() {
  282. return measures;
  283. }
  284. }
  285. }