From 46395126b184c343df5471a147cdb48133f59f6f Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Wed, 23 Apr 2014 14:41:32 +0200 Subject: [PATCH] SONAR-3437, SONAR-5189 Store measures in a persistit cache --- .../issue/CountUnresolvedIssuesDecorator.java | 79 ++++--- .../core/timemachine/VariationDecorator.java | 24 +- .../CountUnresolvedIssuesDecoratorTest.java | 15 +- .../timemachine/VariationDecoratorTest.java | 16 +- .../org/sonar/batch/DefaultTimeMachine.java | 8 +- .../org/sonar/batch/debt/DebtDecorator.java | 3 +- .../java/org/sonar/batch/index/Bucket.java | 42 ---- .../java/org/sonar/batch/index/Cache.java | 31 ++- .../org/sonar/batch/index/DefaultIndex.java | 33 ++- .../index/DefaultPersistenceManager.java | 24 +- .../sonar/batch/index/MeasurePersister.java | 214 ++++-------------- .../sonar/batch/index/MemoryOptimizer.java | 117 ---------- .../sonar/batch/index/PersistenceManager.java | 9 - .../org/sonar/batch/phases/PhaseExecutor.java | 9 +- .../batch/scan/ProjectScanContainer.java | 33 ++- .../batch/scan/measure/MeasureCache.java | 61 +++++ .../batch/scan/measure/package-info.java | 27 +++ .../sonar/batch/debt/DebtDecoratorTest.java | 12 +- .../org/sonar/batch/index/BucketTest.java | 39 ---- .../sonar/batch/index/DefaultIndexTest.java | 12 +- .../batch/index/MeasurePersisterTest.java | 156 ++++--------- .../batch/index/MemoryOptimizerTest.java | 94 -------- .../batch/scan/measure/MeasureCacheTest.java | 83 +++++++ .../java/org/sonar/api/batch/SonarIndex.java | 4 + .../java/org/sonar/api/measures/Measure.java | 25 +- .../sonar/api/measures/MeasuresFilter.java | 3 +- .../sonar/api/measures/MeasuresFilters.java | 44 ++-- .../java/org/sonar/api/measures/Metric.java | 17 +- .../org/sonar/api/measures/RuleMeasure.java | 86 +++++-- .../main/java/org/sonar/api/rules/Rule.java | 55 +++-- .../technicaldebt/batch/Characteristic.java | 3 +- .../api/technicaldebt/batch/Requirement.java | 3 +- .../api/measures/MeasuresFiltersTest.java | 46 ++-- .../org/sonar/api/test/IsRuleMeasure.java | 2 +- 34 files changed, 612 insertions(+), 817 deletions(-) delete mode 100644 sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java delete mode 100644 sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java index 05ad24edf1c..00c145353dd 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java @@ -20,16 +20,32 @@ package org.sonar.plugins.core.issue; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.*; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; import org.apache.commons.lang.time.DateUtils; -import org.sonar.api.batch.*; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; -import org.sonar.api.measures.*; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasureUtils; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; @@ -38,7 +54,12 @@ import org.sonar.batch.components.TimeMachineConfiguration; import javax.annotation.Nullable; -import java.util.*; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; /** * Computes metrics related to number of issues. @@ -80,7 +101,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator { CoreMetrics.OPEN_ISSUES, CoreMetrics.REOPENED_ISSUES, CoreMetrics.CONFIRMED_ISSUES - ); + ); } public void decorate(Resource resource, DecoratorContext context) { @@ -90,7 +111,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator { boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context); Multiset severityBag = HashMultiset.create(); - Map> rulesPerSeverity = Maps.newHashMap(); + Map> rulesPerSeverity = Maps.newHashMap(); ListMultimap issuesPerSeverity = ArrayListMultimap.create(); int countOpen = 0; int countReopened = 0; @@ -98,8 +119,8 @@ public class CountUnresolvedIssuesDecorator implements Decorator { for (Issue issue : issues) { severityBag.add(RulePriority.valueOf(issue.severity())); - Multiset rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity())); - rulesBag.add(rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule())); + Multiset rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity())); + rulesBag.add(issue.ruleKey()); issuesPerSeverity.put(RulePriority.valueOf(issue.severity()), issue); if (Issue.STATUS_OPEN.equals(issue.status())) { @@ -159,22 +180,22 @@ public class CountUnresolvedIssuesDecorator implements Decorator { } } - private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map> rulesPerSeverity) { + private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map> rulesPerSeverity) { Metric metric = SeverityUtils.severityToIssueMetric(severity); Collection children = context.getChildrenMeasures(MeasuresFilters.rules(metric)); for (Measure child : children) { RuleMeasure childRuleMeasure = (RuleMeasure) child; - Rule rule = childRuleMeasure.getRule(); - if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) { - Multiset rulesBag = initRules(rulesPerSeverity, severity); - rulesBag.add(rule, childRuleMeasure.getIntValue()); + RuleKey ruleKey = childRuleMeasure.ruleKey(); + if (ruleKey != null && MeasureUtils.hasValue(childRuleMeasure)) { + Multiset rulesBag = initRules(rulesPerSeverity, severity); + rulesBag.add(ruleKey, childRuleMeasure.getIntValue()); } } - Multiset rulesBag = rulesPerSeverity.get(severity); + Multiset rulesBag = rulesPerSeverity.get(severity); if (rulesBag != null) { - for (Multiset.Entry entry : rulesBag.entrySet()) { + for (Multiset.Entry entry : rulesBag.entrySet()) { RuleMeasure measure = RuleMeasure.createForRule(metric, entry.getElement(), (double) entry.getCount()); measure.setSeverity(severity); context.saveMeasure(measure); @@ -185,34 +206,34 @@ public class CountUnresolvedIssuesDecorator implements Decorator { private void saveNewIssuesPerRule(DecoratorContext context, RulePriority severity, Collection issues, boolean shouldSaveNewMetrics) { if (shouldSaveNewMetrics) { Metric metric = SeverityUtils.severityToNewMetricIssue(severity); - ListMultimap childMeasuresPerRule = ArrayListMultimap.create(); - ListMultimap issuesPerRule = ArrayListMultimap.create(); - Set rules = Sets.newHashSet(); + ListMultimap childMeasuresPerRuleKeys = ArrayListMultimap.create(); + ListMultimap issuesPerRuleKeys = ArrayListMultimap.create(); + Set ruleKeys = Sets.newHashSet(); Collection children = context.getChildrenMeasures(MeasuresFilters.rules(metric)); for (Measure child : children) { RuleMeasure childRuleMeasure = (RuleMeasure) child; - Rule rule = childRuleMeasure.getRule(); - if (rule != null) { - childMeasuresPerRule.put(rule, childRuleMeasure); - rules.add(rule); + RuleKey ruleKey = childRuleMeasure.ruleKey(); + if (ruleKey != null) { + childMeasuresPerRuleKeys.put(ruleKey, childRuleMeasure); + ruleKeys.add(ruleKey); } } for (Issue issue : issues) { if (RulePriority.valueOf(issue.severity()).equals(severity)) { - Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule()); - rules.add(rule); - issuesPerRule.put(rule, issue); + ruleKeys.add(issue.ruleKey()); + issuesPerRuleKeys.put(issue.ruleKey(), issue); } } - for (Rule rule : rules) { + for (RuleKey ruleKey : ruleKeys) { + Rule rule = rulefinder.findByKey(ruleKey); RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null); measure.setSeverity(severity); for (Period period : timeMachineConfiguration.periods()) { int variationIndex = period.getIndex(); - double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + countIssues(issuesPerRule.get(rule), period); + double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRuleKeys.get(rule.ruleKey())) + countIssues(issuesPerRuleKeys.get(rule.ruleKey()), period); measure.setVariation(variationIndex, sum); } context.saveMeasure(measure); @@ -242,8 +263,8 @@ public class CountUnresolvedIssuesDecorator implements Decorator { return sum; } - private Multiset initRules(Map> rulesPerSeverity, RulePriority severity) { - Multiset rulesBag = rulesPerSeverity.get(severity); + private Multiset initRules(Map> rulesPerSeverity, RulePriority severity) { + Multiset rulesBag = rulesPerSeverity.get(severity); if (rulesBag == null) { rulesBag = HashMultiset.create(); rulesPerSeverity.put(severity, rulesBag); diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java index efebe9d7a62..30f5edcd082 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java @@ -21,12 +21,21 @@ package org.sonar.plugins.core.timemachine; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.*; -import org.sonar.api.measures.*; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorBarriers; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.DependedUpon; +import org.sonar.api.batch.DependsUpon; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; +import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Project; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Resource; import org.sonar.api.resources.Scopes; +import org.sonar.api.rules.RuleFinder; import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.batch.components.PastMeasuresLoader; import org.sonar.batch.components.PastSnapshot; @@ -42,16 +51,17 @@ public class VariationDecorator implements Decorator { private List projectPastSnapshots; private MetricFinder metricFinder; private PastMeasuresLoader pastMeasuresLoader; + private RuleFinder ruleFinder; - - public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration timeMachineConfiguration) { - this(pastMeasuresLoader, metricFinder, timeMachineConfiguration.getProjectPastSnapshots()); + public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration timeMachineConfiguration, RuleFinder ruleFinder) { + this(pastMeasuresLoader, metricFinder, timeMachineConfiguration.getProjectPastSnapshots(), ruleFinder); } - VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List projectPastSnapshots) { + VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List projectPastSnapshots, RuleFinder ruleFinder) { this.pastMeasuresLoader = pastMeasuresLoader; this.projectPastSnapshots = projectPastSnapshots; this.metricFinder = metricFinder; + this.ruleFinder = ruleFinder; } public boolean shouldExecuteOnProject(Project project) { @@ -98,7 +108,7 @@ public class VariationDecorator implements Decorator { Characteristic characteristic = measure.getCharacteristic(); Integer characteristicId = characteristic != null ? characteristic.id() : null; Integer personId = measure.getPersonId(); - Integer ruleId = measure instanceof RuleMeasure ? ((RuleMeasure) measure).getRule().getId() : null; + Integer ruleId = measure instanceof RuleMeasure ? ruleFinder.findByKey(((RuleMeasure) measure).ruleKey()).getId() : null; Object[] pastMeasure = pastMeasuresByKey.get(new MeasureKey(metricId, characteristicId, personId, ruleId)); if (updateVariation(measure, pastMeasure, index)) { diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java index 41241336bd0..4ee5cb5934b 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java @@ -31,7 +31,11 @@ import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.measures.*; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.Scopes; @@ -55,7 +59,12 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyDouble; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; public class CountUnresolvedIssuesDecoratorTest { @@ -324,7 +333,7 @@ public class CountUnresolvedIssuesDecoratorTest { } RuleMeasure m = (RuleMeasure) o; return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(rule, m.getRule()) && + ObjectUtils.equals(rule.ruleKey(), m.ruleKey()) && ObjectUtils.equals(var1, m.getVariation1()) && ObjectUtils.equals(var2, m.getVariation2()); } diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java index fe48596751c..c7c1876bf6c 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java @@ -32,6 +32,7 @@ import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; import org.sonar.batch.components.PastMeasuresLoader; import org.sonar.batch.components.PastSnapshot; import org.sonar.batch.components.TimeMachineConfiguration; @@ -60,7 +61,7 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { @Test public void shouldComputeVariations() { TimeMachineConfiguration timeMachineConfiguration = mock(TimeMachineConfiguration.class); - VariationDecorator decorator = new VariationDecorator(mock(PastMeasuresLoader.class), mock(MetricFinder.class), timeMachineConfiguration); + VariationDecorator decorator = new VariationDecorator(mock(PastMeasuresLoader.class), mock(MetricFinder.class), timeMachineConfiguration, mock(RuleFinder.class)); assertThat(decorator.shouldComputeVariation(new Project("foo"))).isTrue(); assertThat(decorator.shouldComputeVariation(new File("foo/bar.c"))).isFalse(); @@ -89,7 +90,7 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { Measure currentCoverage = newMeasure(COVERAGE, 80.0); when(context.getMeasures(Matchers.anyObject())).thenReturn(Arrays.asList(currentNcloc, currentCoverage)); - VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3)); + VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3), mock(RuleFinder.class)); decorator.decorate(dir, context); // context updated for each variation : 2 times for ncloc and 1 time for coverage @@ -106,11 +107,16 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { @Test public void shouldComputeVariationOfRuleMeasures() { - Rule rule1 = Rule.create(); + RuleFinder ruleFinder = mock(RuleFinder.class); + + Rule rule1 = Rule.create("repo", "rule1"); rule1.setId(1); - Rule rule2 = Rule.create(); + Rule rule2 = Rule.create("repo", "rule2"); rule2.setId(2); + when(ruleFinder.findByKey(rule1.ruleKey())).thenReturn(rule1); + when(ruleFinder.findByKey(rule2.ruleKey())).thenReturn(rule2); + Resource dir = new Directory("org/foo"); PastMeasuresLoader pastMeasuresLoader = mock(PastMeasuresLoader.class); @@ -129,7 +135,7 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { Measure violationsRule2 = RuleMeasure.createForRule(VIOLATIONS, rule2, 70.0); when(context.getMeasures(Matchers.anyObject())).thenReturn(Arrays.asList(violations, violationsRule1, violationsRule2)); - VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1)); + VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1), ruleFinder); decorator.decorate(dir, context); // context updated for each variation diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java index 466be91dc79..565417efad0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java @@ -38,7 +38,12 @@ import org.sonar.batch.index.DefaultIndex; import javax.annotation.Nullable; import javax.persistence.Query; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; public class DefaultTimeMachine implements TimeMachine { @@ -155,7 +160,6 @@ public class DefaultTimeMachine implements TimeMachine { static Measure toMeasure(MeasureModel model, Metric metric, @Nullable Characteristic characteristic) { // NOTE: measures on rule are not supported Measure measure = new Measure(metric); - measure.setId(model.getId()); measure.setDescription(model.getDescription()); measure.setValue(model.getValue()); measure.setData(model.getData(metric)); diff --git a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java index 71e9cc36a1c..17475936308 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java @@ -48,6 +48,7 @@ import org.sonar.api.technicaldebt.batch.TechnicalDebtModel; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.util.Arrays; import java.util.List; import java.util.Map; @@ -110,7 +111,7 @@ public final class DebtDecorator implements Decorator { for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) { Long debt = measure.getValue().longValue(); RuleMeasure ruleMeasure = (RuleMeasure) measure; - total += computeDebt(debt, ruleMeasure.getRule().ruleKey(), ruleDebts, characteristicDebts); + total += computeDebt(debt, ruleMeasure.ruleKey(), ruleDebts, characteristicDebts); } context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Bucket.java b/sonar-batch/src/main/java/org/sonar/batch/index/Bucket.java index af6a87ae8a6..ea4ed3b11a0 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/Bucket.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/Bucket.java @@ -19,24 +19,15 @@ */ package org.sonar.batch.index; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilter; -import org.sonar.api.measures.MeasuresFilters; import org.sonar.api.resources.Resource; -import org.sonar.api.utils.SonarException; -import java.util.Collection; import java.util.Collections; import java.util.List; public final class Bucket { private Resource resource; - private ListMultimap measuresByMetric = ArrayListMultimap.create(); private Bucket parent; private List children; @@ -79,30 +70,7 @@ public final class Bucket { return parent; } - public void addMeasure(Measure measure) { - List metricMeasures = measuresByMetric.get(measure.getMetric().getKey()); - - boolean add = true; - if (metricMeasures != null) { - int index = metricMeasures.indexOf(measure); - if (index > -1) { - if (metricMeasures.get(index) == measure) { - add = false; - } else if (measure.getMetric().equals(CoreMetrics.TESTS)) { - // Hack for SONAR-5212 - measuresByMetric.remove(measure.getMetric().getKey(), metricMeasures.get(index)); - } else { - throw new SonarException("Can not add twice the same measure on " + resource + ": " + measure); - } - } - } - if (add) { - measuresByMetric.put(measure.getMetric().getKey(), measure); - } - } - public void clear() { - measuresByMetric = null; children = null; if (parent != null) { parent.removeChild(this); @@ -110,16 +78,6 @@ public final class Bucket { } } - public M getMeasures(final MeasuresFilter filter) { - Collection unfiltered; - if (filter instanceof MeasuresFilters.MetricFilter) { - unfiltered = measuresByMetric.get(((MeasuresFilters.MetricFilter) filter).filterOnMetricKey()); - } else { - unfiltered = measuresByMetric.values(); - } - return filter.filter(unfiltered); - } - @Override public boolean equals(Object o) { if (this == o) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java index 46f6bbf0a84..13e238fce5b 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/Cache.java @@ -26,6 +26,7 @@ import com.persistit.exception.PersistitException; import org.apache.commons.lang.builder.ToStringBuilder; import javax.annotation.CheckForNull; + import java.io.Serializable; import java.util.Iterator; import java.util.Set; @@ -219,7 +220,6 @@ public class Cache { } } - /** * Clears the default as well as all group caches. */ @@ -293,6 +293,20 @@ public class Cache { } } + /** + * Lazy-loading values for given keys + */ + public Iterable values(Object firstKey, Object secondKey) { + try { + exchange.clear(); + exchange.append(firstKey).append(secondKey).append(Key.BEFORE); + Exchange iteratorExchange = new Exchange(exchange); + return new ValueIterable(iteratorExchange, false); + } catch (Exception e) { + throw new IllegalStateException("Fail to get values from cache " + name, e); + } + } + /** * Lazy-loading values for a given key */ @@ -307,6 +321,20 @@ public class Cache { } } + /** + * Lazy-loading values for a given key + */ + public Iterable allValues(Object key) { + try { + exchange.clear(); + exchange.append(key).append(Key.BEFORE); + Exchange iteratorExchange = new Exchange(exchange); + return new ValueIterable(iteratorExchange, true); + } catch (Exception e) { + throw new IllegalStateException("Fail to get values from cache " + name, e); + } + } + /** * Lazy-loading values */ @@ -352,7 +380,6 @@ public class Cache { } } - // // LAZY ITERATORS AND ITERABLES // diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java index 9624513e288..cea7aa71729 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java @@ -52,6 +52,7 @@ import org.sonar.api.violations.ViolationQuery; import org.sonar.batch.ProjectTree; import org.sonar.batch.issue.DeprecatedViolations; import org.sonar.batch.issue.ModuleIssues; +import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.core.component.ComponentKeys; import org.sonar.core.component.ScanGraph; @@ -85,17 +86,19 @@ public class DefaultIndex extends SonarIndex { private ProjectTree projectTree; private final DeprecatedViolations deprecatedViolations; private ModuleIssues moduleIssues; + private final MeasureCache measureCache; private ResourceKeyMigration migration; public DefaultIndex(PersistenceManager persistence, ProjectTree projectTree, MetricFinder metricFinder, - ScanGraph graph, DeprecatedViolations deprecatedViolations, ResourceKeyMigration migration) { + ScanGraph graph, DeprecatedViolations deprecatedViolations, ResourceKeyMigration migration, MeasureCache measureCache) { this.persistence = persistence; this.projectTree = projectTree; this.metricFinder = metricFinder; this.graph = graph; this.deprecatedViolations = deprecatedViolations; this.migration = migration; + this.measureCache = measureCache; } public void start() { @@ -174,24 +177,20 @@ public class DefaultIndex extends SonarIndex { @Override public Measure getMeasure(Resource resource, Metric metric) { - Bucket bucket = buckets.get(resource); - if (bucket != null) { - Measure measure = bucket.getMeasures(MeasuresFilters.metric(metric)); - if (measure != null) { - return persistence.reloadMeasure(measure); - } - } - return null; + return getMeasures(resource, MeasuresFilters.metric(metric)); } @Override public M getMeasures(Resource resource, MeasuresFilter filter) { - Bucket bucket = buckets.get(resource); - if (bucket != null) { - // TODO the data measures which are not kept in memory are not reloaded yet. Use getMeasure(). - return bucket.getMeasures(filter); + // Reload resource so that effective key is populated + Resource indexedResource = getResource(resource); + Iterable unfiltered; + if (filter instanceof MeasuresFilters.MetricFilter) { + unfiltered = measureCache.byMetric(indexedResource, ((MeasuresFilters.MetricFilter) filter).filterOnMetricKey()); + } else { + unfiltered = measureCache.byResource(indexedResource); } - return null; + return filter.filter(unfiltered); } /** @@ -206,11 +205,7 @@ public class DefaultIndex extends SonarIndex { throw new SonarException("Unknown metric: " + measure.getMetricKey()); } measure.setMetric(metric); - bucket.addMeasure(measure); - - if (measure.getPersistenceMode().useDatabase()) { - persistence.saveMeasure(bucket.getResource(), measure); - } + measureCache.put(resource, measure); } return measure; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java index 14da97381cf..39b9d8d472a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java @@ -22,7 +22,6 @@ package org.sonar.batch.index; import org.sonar.api.batch.Event; import org.sonar.api.database.model.Snapshot; import org.sonar.api.design.Dependency; -import org.sonar.api.measures.Measure; import org.sonar.api.resources.Project; import org.sonar.api.resources.ProjectLink; import org.sonar.api.resources.Resource; @@ -34,17 +33,14 @@ public final class DefaultPersistenceManager implements PersistenceManager { private ResourcePersister resourcePersister; private SourcePersister sourcePersister; - private MeasurePersister measurePersister; private DependencyPersister dependencyPersister; private LinkPersister linkPersister; private EventPersister eventPersister; public DefaultPersistenceManager(ResourcePersister resourcePersister, SourcePersister sourcePersister, - MeasurePersister measurePersister, DependencyPersister dependencyPersister, - LinkPersister linkPersister, EventPersister eventPersister) { + DependencyPersister dependencyPersister, LinkPersister linkPersister, EventPersister eventPersister) { this.resourcePersister = resourcePersister; this.sourcePersister = sourcePersister; - this.measurePersister = measurePersister; this.dependencyPersister = dependencyPersister; this.linkPersister = linkPersister; this.eventPersister = eventPersister; @@ -55,14 +51,6 @@ public final class DefaultPersistenceManager implements PersistenceManager { sourcePersister.clear(); } - public void setDelayedMode(boolean b) { - measurePersister.setDelayedMode(b); - } - - public void dump() { - measurePersister.dump(); - } - public void saveProject(Project project, Project parent) { resourcePersister.saveProject(project, parent); } @@ -82,16 +70,6 @@ public final class DefaultPersistenceManager implements PersistenceManager { return sourcePersister.getSource(resource); } - public void saveMeasure(Resource resource, Measure measure) { - if (ResourceUtils.isPersistable(resource)) { - measurePersister.saveMeasure(resource, measure); - } - } - - public Measure reloadMeasure(Measure measure) { - return measurePersister.reloadMeasure(measure); - } - public void saveDependency(Project project, Dependency dependency, Dependency parentDependency) { if (ResourceUtils.isPersistable(dependency.getFrom()) && ResourceUtils.isPersistable(dependency.getTo())) { dependencyPersister.saveDependency(project, dependency, parentDependency); diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java index 8e61a931bd0..80cac3ab4cb 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java @@ -20,94 +20,69 @@ package org.sonar.batch.index; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.SetMultimap; import org.apache.ibatis.session.SqlSession; -import org.slf4j.LoggerFactory; import org.sonar.api.database.model.MeasureMapper; import org.sonar.api.database.model.MeasureModel; import org.sonar.api.database.model.Snapshot; -import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.RuleMeasure; import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.api.utils.SonarException; +import org.sonar.batch.index.Cache.Entry; +import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.core.persistence.MyBatis; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -public final class MeasurePersister { +public final class MeasurePersister implements ScanPersister { private final MyBatis mybatis; - private final ResourcePersister resourcePersister; private final RuleFinder ruleFinder; - private final MemoryOptimizer memoryOptimizer; - private final SetMultimap unsavedMeasuresByResource = LinkedHashMultimap.create(); - private boolean delayedMode = false; + private final MeasureCache measureCache; + private final SnapshotCache snapshotCache; + private final ResourceCache resourceCache; - public MeasurePersister(MyBatis mybatis, ResourcePersister resourcePersister, RuleFinder ruleFinder, MemoryOptimizer memoryOptimizer) { + public MeasurePersister(MyBatis mybatis, RuleFinder ruleFinder, + MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache) { this.mybatis = mybatis; - this.resourcePersister = resourcePersister; this.ruleFinder = ruleFinder; - this.memoryOptimizer = memoryOptimizer; - } - - public void setDelayedMode(boolean delayedMode) { - this.delayedMode = delayedMode; + this.measureCache = measureCache; + this.snapshotCache = snapshotCache; + this.resourceCache = resourceCache; } - public Measure reloadMeasure(Measure measure) { - return memoryOptimizer.reloadMeasure(measure); - } - - public void dump() { - LoggerFactory.getLogger(getClass()).debug("{} measures to dump", unsavedMeasuresByResource.size()); + @Override + public void persist() { + SqlSession session = mybatis.openSession(); + try { + MeasureMapper mapper = session.getMapper(MeasureMapper.class); - insert(getMeasuresToSave()); - } + for (Entry entry : measureCache.entries()) { + String effectiveKey = entry.key()[0].toString(); + Measure measure = entry.value(); + Resource resource = resourceCache.get(effectiveKey); - public void saveMeasure(Resource resource, Measure measure) { - if (shouldSaveLater(measure)) { - if (measure.getMetric().equals(CoreMetrics.TESTS) && unsavedMeasuresByResource.get(resource).contains(measure)) { - // Hack for SONAR-5212 - unsavedMeasuresByResource.remove(resource, measure); + if (shouldPersistMeasure(resource, measure)) { + Snapshot snapshot = snapshotCache.get(effectiveKey); + MeasureModel measureModel = model(measure).setSnapshotId(snapshot.getId()); + try { + mapper.insert(measureModel); + if (measureModel.getMeasureData() != null) { + mapper.insertData(measureModel.getMeasureData()); + } + } catch (Exception e) { + // SONAR-4066 + throw new SonarException(String.format("Unable to save measure for metric [%s] on component [%s]", measure.getMetricKey(), resource.getKey()), e); + } + } } - unsavedMeasuresByResource.put(resource, measure); - return; - } - MeasureModel model; - try { - model = insertOrUpdate(resource, measure); - } catch (Exception e) { - // SONAR-4066 - throw new SonarException(String.format("Unable to save measure for metric [%s] on component [%s]", measure.getMetricKey(), resource.getKey()), e); - } - if (model != null) { - memoryOptimizer.evictDataMeasure(measure, model); - } - } - private MeasureModel insertOrUpdate(Resource resource, Measure measure) { - Snapshot snapshot = resourcePersister.getSnapshotOrFail(resource); - if (measure.getId() != null) { - return update(measure, snapshot); - } - if (shouldPersistMeasure(resource, measure)) { - MeasureModel insert = insert(measure, snapshot); - measure.setId(insert.getId()); - return insert; + session.commit(); + } finally { + MyBatis.closeQuietly(session); } - return null; - } - - private boolean shouldSaveLater(Measure measure) { - return delayedMode && measure.getPersistenceMode().useMemory(); } @VisibleForTesting @@ -125,24 +100,6 @@ public final class MeasurePersister { || isNotEmpty; } - private List getMeasuresToSave() { - List measures = Lists.newArrayList(); - - Map> map = unsavedMeasuresByResource.asMap(); - for (Map.Entry> entry : map.entrySet()) { - Resource resource = entry.getKey(); - Snapshot snapshot = resourcePersister.getSnapshot(entry.getKey()); - for (Measure measure : entry.getValue()) { - if (shouldPersistMeasure(resource, measure)) { - measures.add(new MeasureModelAndDetails(model(measure).setSnapshotId(snapshot.getId()), resource.getKey(), measure.getMetricKey())); - } - } - } - - unsavedMeasuresByResource.clear(); - return measures; - } - private MeasureModel model(Measure measure) { MeasureModel model = new MeasureModel(); // we assume that the index has updated the metric @@ -172,9 +129,9 @@ public final class MeasurePersister { if (measure instanceof RuleMeasure) { RuleMeasure ruleMeasure = (RuleMeasure) measure; model.setRulePriority(ruleMeasure.getSeverity()); - Rule rule = ruleMeasure.getRule(); - if (rule != null) { - Rule ruleWithId = ruleFinder.findByKey(rule.getRepositoryKey(), rule.getKey()); + RuleKey ruleKey = ruleMeasure.ruleKey(); + if (ruleKey != null) { + Rule ruleWithId = ruleFinder.findByKey(ruleKey); if (ruleWithId == null) { throw new SonarException("Can not save a measure with unknown rule " + ruleMeasure); } @@ -184,95 +141,4 @@ public final class MeasurePersister { return model; } - private void insert(Iterable values) { - SqlSession session = mybatis.openSession(); - try { - MeasureMapper mapper = session.getMapper(MeasureMapper.class); - - for (MeasureModelAndDetails value : values) { - try { - mapper.insert(value.getMeasureModel()); - if (value.getMeasureModel().getMeasureData() != null) { - mapper.insertData(value.getMeasureModel().getMeasureData()); - } - } catch (Exception e) { - // SONAR-4066 - throw new SonarException(String.format("Unable to save measure for metric [%s] on component [%s]", value.getMetricKey(), value.getResourceKey()), e); - } - } - - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } - } - - private MeasureModel insert(Measure measure, Snapshot snapshot) { - MeasureModel value = model(measure); - value.setSnapshotId(snapshot.getId()); - - SqlSession session = mybatis.openSession(); - try { - MeasureMapper mapper = session.getMapper(MeasureMapper.class); - - mapper.insert(value); - if (value.getMeasureData() != null) { - mapper.insertData(value.getMeasureData()); - } - - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } - - return value; - } - - private MeasureModel update(Measure measure, Snapshot snapshot) { - MeasureModel value = model(measure); - value.setId(measure.getId()); - value.setSnapshotId(snapshot.getId()); - - SqlSession session = mybatis.openSession(); - try { - MeasureMapper mapper = session.getMapper(MeasureMapper.class); - - mapper.update(value); - mapper.deleteData(value); - if (value.getMeasureData() != null) { - mapper.insertData(value.getMeasureData()); - } - - session.commit(); - } finally { - MyBatis.closeQuietly(session); - } - - return value; - } - - // SONAR-4066 - private static class MeasureModelAndDetails { - private final MeasureModel measureModel; - private final String resourceKey; - private final String metricKey; - - public MeasureModelAndDetails(MeasureModel measureModel, String resourceKey, String metricKey) { - this.measureModel = measureModel; - this.resourceKey = resourceKey; - this.metricKey = metricKey; - } - - public MeasureModel getMeasureModel() { - return measureModel; - } - - public String getResourceKey() { - return resourceKey; - } - - public String getMetricKey() { - return metricKey; - } - } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java b/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java deleted file mode 100644 index 9daa941b823..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java +++ /dev/null @@ -1,117 +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.index; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.events.DecoratorExecutionHandler; -import org.sonar.api.batch.events.DecoratorsPhaseHandler; -import org.sonar.api.batch.events.SensorExecutionHandler; -import org.sonar.api.database.DatabaseSession; -import org.sonar.api.database.model.MeasureData; -import org.sonar.api.database.model.MeasureModel; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.PersistenceMode; - -import java.util.List; -import java.util.Map; - -/** - * @since 2.7 - */ -public class MemoryOptimizer implements SensorExecutionHandler, DecoratorExecutionHandler, DecoratorsPhaseHandler { - - private static final Logger LOG = LoggerFactory.getLogger(MemoryOptimizer.class); - - private List loadedMeasures = Lists.newArrayList(); - private Map dataIdByMeasureId = Maps.newHashMap(); - private DatabaseSession session; - - public MemoryOptimizer(DatabaseSession session) { - this.session = session; - } - - /** - * Remove data of a database measure from memory. - */ - public void evictDataMeasure(Measure measure, MeasureModel model) { - if (PersistenceMode.DATABASE.equals(measure.getPersistenceMode())) { - MeasureData data = model.getMeasureData(); - if (data != null && data.getId() != null) { - measure.unsetData(); - dataIdByMeasureId.put(measure.getId(), data.getId()); - } - } - } - - public Measure reloadMeasure(Measure measure) { - if (measure.getId() != null && dataIdByMeasureId.containsKey(measure.getId()) && !measure.hasData()) { - Integer dataId = dataIdByMeasureId.get(measure.getId()); - MeasureData data = session.getSingleResult(MeasureData.class, "id", dataId); - if (data == null) { - LOG.error("The MEASURE_DATA row with id {} is lost", dataId); - - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Reload the data measure: {}, id={}", measure.getMetricKey(), measure.getId()); - } - measure.setData(data.getText()); - loadedMeasures.add(measure); - } - } - return measure; - } - - public void flushMemory() { - if (LOG.isDebugEnabled() && !loadedMeasures.isEmpty()) { - LOG.debug("Flush {} data measures from memory: ", loadedMeasures.size()); - } - for (Measure measure : loadedMeasures) { - measure.unsetData(); - } - loadedMeasures.clear(); - } - - boolean isTracked(Long measureId) { - return dataIdByMeasureId.get(measureId) != null; - } - - public void onSensorExecution(SensorExecutionEvent event) { - if (event.isEnd()) { - flushMemory(); - session.commit(); - } - } - - public void onDecoratorExecution(DecoratorExecutionEvent event) { - if (event.isEnd()) { - flushMemory(); - } - } - - public void onDecoratorsPhase(DecoratorsPhaseEvent event) { - if (event.isEnd()) { - session.commit(); - } - } - -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java b/sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java index d42e1a9f2e1..d90f63d8b66 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java +++ b/sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java @@ -22,7 +22,6 @@ package org.sonar.batch.index; import org.sonar.api.batch.Event; import org.sonar.api.database.model.Snapshot; import org.sonar.api.design.Dependency; -import org.sonar.api.measures.Measure; import org.sonar.api.resources.Project; import org.sonar.api.resources.ProjectLink; import org.sonar.api.resources.Resource; @@ -32,10 +31,6 @@ import java.util.List; public interface PersistenceManager { void clear(); - void setDelayedMode(boolean b); - - void dump(); - void saveProject(Project project, Project parent); Snapshot saveResource(Project project, Resource resource, Resource parent); @@ -44,10 +39,6 @@ public interface PersistenceManager { String getSource(Resource resource); - void saveMeasure(Resource resource, Measure measure); - - Measure reloadMeasure(Measure measure); - void saveDependency(Project project, Dependency dependency, Dependency parentDependency); void saveLink(Project project, ProjectLink link); diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java index 0ba16ea66d7..90bafa7dc3e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java @@ -116,8 +116,6 @@ public final class PhaseExecutor { executeInitializersPhase(); - persistenceManager.setDelayedMode(true); - if (phases.isEnabled(Phases.Phase.SENSOR)) { // Index and lock the filesystem fs.index(); @@ -135,12 +133,6 @@ public final class PhaseExecutor { decoratorsExecutor.execute(); } - String saveMeasures = "Save measures"; - eventBus.fireEvent(new BatchStepEvent(saveMeasures, true)); - persistenceManager.dump(); - eventBus.fireEvent(new BatchStepEvent(saveMeasures, false)); - persistenceManager.setDelayedMode(false); - if (module.isRoot()) { jsonReport.execute(); @@ -162,6 +154,7 @@ public final class PhaseExecutor { LOGGER.debug("Execute {}", persister.getClass().getName()); persister.persist(); } + eventBus.fireEvent(new BatchStepEvent(persistersStep, false)); } 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 e4eacf6aa8d..335884d7f87 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 @@ -21,7 +21,6 @@ package org.sonar.batch.scan; import com.google.common.annotations.VisibleForTesting; import org.sonar.api.BatchComponent; -import org.sonar.api.BatchExtension; import org.sonar.api.CoreProperties; import org.sonar.api.batch.InstantiationStrategy; import org.sonar.api.batch.bootstrap.ProjectBootstrapper; @@ -36,18 +35,40 @@ import org.sonar.batch.DefaultFileLinesContextFactory; import org.sonar.batch.DefaultResourceCreationLock; import org.sonar.batch.ProjectConfigurator; import org.sonar.batch.ProjectTree; -import org.sonar.batch.bootstrap.*; +import org.sonar.batch.bootstrap.BootstrapSettings; +import org.sonar.batch.bootstrap.ExtensionInstaller; +import org.sonar.batch.bootstrap.ExtensionMatcher; +import org.sonar.batch.bootstrap.ExtensionUtils; +import org.sonar.batch.bootstrap.MetricProvider; import org.sonar.batch.components.PeriodsDefinition; import org.sonar.batch.debt.DebtModelProvider; import org.sonar.batch.debt.IssueChangelogDebtCalculator; -import org.sonar.batch.index.*; -import org.sonar.batch.issue.*; +import org.sonar.batch.index.Caches; +import org.sonar.batch.index.ComponentDataCache; +import org.sonar.batch.index.ComponentDataPersister; +import org.sonar.batch.index.DefaultIndex; +import org.sonar.batch.index.DefaultPersistenceManager; +import org.sonar.batch.index.DefaultResourcePersister; +import org.sonar.batch.index.DependencyPersister; +import org.sonar.batch.index.EventPersister; +import org.sonar.batch.index.LinkPersister; +import org.sonar.batch.index.MeasurePersister; +import org.sonar.batch.index.ResourceCache; +import org.sonar.batch.index.ResourceKeyMigration; +import org.sonar.batch.index.SnapshotCache; +import org.sonar.batch.index.SourcePersister; +import org.sonar.batch.issue.DefaultProjectIssues; +import org.sonar.batch.issue.DeprecatedViolations; +import org.sonar.batch.issue.IssueCache; +import org.sonar.batch.issue.IssuePersister; +import org.sonar.batch.issue.ScanIssueStorage; import org.sonar.batch.phases.GraphPersister; import org.sonar.batch.profiling.PhasesSumUpTimeProfiler; import org.sonar.batch.rule.RulesProvider; import org.sonar.batch.scan.filesystem.InputFileCache; import org.sonar.batch.scan.maven.FakeMavenPluginExecutor; import org.sonar.batch.scan.maven.MavenPluginExecutor; +import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.batch.source.HighlightableBuilder; import org.sonar.batch.source.SymbolizableBuilder; import org.sonar.core.component.ScanGraph; @@ -112,7 +133,6 @@ public class ProjectScanContainer extends ComponentContainer { EventPersister.class, LinkPersister.class, MeasurePersister.class, - MemoryOptimizer.class, DefaultResourcePersister.class, SourcePersister.class, DefaultNotificationManager.class, @@ -169,6 +189,9 @@ public class ProjectScanContainer extends ComponentContainer { // Differential periods PeriodsDefinition.class, + // Measures + MeasureCache.class, + ProjectSettingsReady.class); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java new file mode 100644 index 00000000000..1f668614d8f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java @@ -0,0 +1,61 @@ +/* + * 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.scan.measure; + +import com.google.common.base.Preconditions; +import org.sonar.api.BatchComponent; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Resource; +import org.sonar.batch.index.Cache; +import org.sonar.batch.index.Cache.Entry; +import org.sonar.batch.index.Caches; + +/** + * Cache of all measures. This cache is shared amongst all project modules. + */ +public class MeasureCache implements BatchComponent { + + private final Cache cache; + + public MeasureCache(Caches caches) { + cache = caches.createCache("measures"); + } + + public Iterable> entries() { + return cache.entries(); + } + + public Iterable byResource(Resource r) { + return cache.allValues(r.getEffectiveKey()); + } + + public MeasureCache put(Resource resource, Measure measure) { + Preconditions.checkNotNull(resource.getEffectiveKey()); + Preconditions.checkNotNull(measure.getMetricKey()); + cache.put(resource.getEffectiveKey(), measure.getMetricKey(), measure.hashCode(), measure); + return this; + } + + public Iterable byMetric(Resource resource, String metricKey) { + Preconditions.checkNotNull(resource.getEffectiveKey()); + Preconditions.checkNotNull(metricKey); + return cache.values(resource.getEffectiveKey(), metricKey); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java new file mode 100644 index 00000000000..64f9c876250 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * This package is a part of bootstrap process, so we should take care about backward compatibility. + */ +@ParametersAreNonnullByDefault +package org.sonar.batch.scan.measure; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java index fc82ece746c..6edbbab8fbf 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java @@ -59,7 +59,7 @@ import java.util.Collections; import static com.google.common.collect.Lists.newArrayList; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.argThat; +import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -204,7 +204,7 @@ public class DebtDecoratorTest { new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null) .setValue(5d * ONE_DAY_IN_MINUTES) - )); + )); decorator.decorate(resource, context); verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES); @@ -225,7 +225,7 @@ public class DebtDecoratorTest { new RuleMeasure(CoreMetrics.TECHNICAL_DEBT, org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule()) , null, null).setValue(10d * ONE_DAY_IN_MINUTES) - )); + )); decorator.decorate(resource, context); verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES); @@ -319,8 +319,7 @@ public class DebtDecoratorTest { description.appendText(new StringBuilder() .append("value=").append(value).append(",") .append("characteristic=").append(characteristic.key()).append(",") - .append("metric=").append(metric.getKey()).toString()) - ; + .append("metric=").append(metric.getKey()).toString()); } } @@ -342,8 +341,7 @@ public class DebtDecoratorTest { } RuleMeasure m = (RuleMeasure) o; return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) && - ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) && + ObjectUtils.equals(ruleKey, m.ruleKey()) && ObjectUtils.equals(value, m.getValue()); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/BucketTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/BucketTest.java index 2fc521a64d5..cb20f15cde6 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/BucketTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/BucketTest.java @@ -20,12 +20,9 @@ package org.sonar.batch.index; import org.junit.Test; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.MeasuresFilters; import org.sonar.api.measures.Metric; import org.sonar.api.resources.Directory; import org.sonar.api.resources.File; -import org.sonar.api.utils.SonarException; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.core.IsNot.not; @@ -51,42 +48,6 @@ public class BucketTest { assertThat(packageBucket.getChildren(), hasItem(fileBucket)); } - @Test - public void shouldAddNewMeasure() { - Bucket fileBucket = new Bucket(javaFile); - Measure measure = new Measure(ncloc).setValue(1200.0); - fileBucket.addMeasure(measure); - - assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1)); - assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)), is(measure)); - } - - @Test - public void shouldUpdateMeasure() { - Bucket fileBucket = new Bucket(javaFile); - Measure measure = new Measure(ncloc).setValue(1200.0); - fileBucket.addMeasure(measure); - - assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1)); - assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)).getValue(), is(1200.0)); - - measure.setValue(500.0); - fileBucket.addMeasure(measure); - - assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1)); - assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)).getValue(), is(500.0)); - } - - @Test(expected = SonarException.class) - public void shouldFailIfAddingSameMeasures() { - Bucket fileBucket = new Bucket(javaFile); - Measure measure = new Measure(ncloc).setValue(1200.0); - fileBucket.addMeasure(measure); - - measure = new Measure(ncloc).setValue(500.0); - fileBucket.addMeasure(measure); - } - @Test public void shouldBeEquals() { assertEquals(new Bucket(directory), new Bucket(directory)); diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java index 4abc71694cf..9a162fbf425 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java @@ -28,7 +28,13 @@ import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilters; import org.sonar.api.measures.MetricFinder; import org.sonar.api.profiles.RulesProfile; -import org.sonar.api.resources.*; +import org.sonar.api.resources.Directory; +import org.sonar.api.resources.File; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Library; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Resource; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.Violation; @@ -36,6 +42,7 @@ import org.sonar.api.violations.ViolationQuery; import org.sonar.batch.ProjectTree; import org.sonar.batch.issue.DeprecatedViolations; import org.sonar.batch.issue.ModuleIssues; +import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.core.component.ScanGraph; import java.io.IOException; @@ -68,7 +75,8 @@ public class DefaultIndexTest { ruleFinder = mock(RuleFinder.class); ProjectTree projectTree = mock(ProjectTree.class); - index = new DefaultIndex(mock(PersistenceManager.class), projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, mock(ResourceKeyMigration.class)); + index = new DefaultIndex(mock(PersistenceManager.class), projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, mock(ResourceKeyMigration.class), + mock(MeasureCache.class)); java.io.File baseDir = temp.newFolder(); project = new Project("project"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java index fc3a69628d2..9785c944b7a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java @@ -23,8 +23,6 @@ import org.apache.commons.lang.StringUtils; import org.junit.Before; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.mockito.ArgumentCaptor; -import org.sonar.api.database.model.MeasureModel; import org.sonar.api.database.model.Snapshot; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; @@ -38,13 +36,13 @@ import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.RulePriority; import org.sonar.api.utils.SonarException; +import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.core.persistence.AbstractDaoTestCase; +import java.util.Arrays; + import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class MeasurePersisterTest extends AbstractDaoTestCase { @@ -63,22 +61,28 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { MeasurePersister measurePersister; RuleFinder ruleFinder = mock(RuleFinder.class); - ResourcePersister resourcePersister = mock(ResourcePersister.class); - MemoryOptimizer memoryOptimizer = mock(MemoryOptimizer.class); Project project = new Project("foo"); Directory aDirectory = new Directory("org/foo"); File aFile = new File("org/foo/Bar.java"); Snapshot projectSnapshot = snapshot(PROJECT_SNAPSHOT_ID); Snapshot packageSnapshot = snapshot(PACKAGE_SNAPSHOT_ID); + private SnapshotCache snapshotCache; + + private MeasureCache measureCache; + @Before public void mockResourcePersister() { - when(resourcePersister.getSnapshotOrFail(project)).thenReturn(projectSnapshot); - when(resourcePersister.getSnapshotOrFail(aDirectory)).thenReturn(packageSnapshot); - when(resourcePersister.getSnapshot(project)).thenReturn(projectSnapshot); - when(resourcePersister.getSnapshot(aDirectory)).thenReturn(packageSnapshot); + snapshotCache = mock(SnapshotCache.class); + measureCache = mock(MeasureCache.class); + ResourceCache resourceCache = mock(ResourceCache.class); + when(snapshotCache.get("foo")).thenReturn(projectSnapshot); + when(snapshotCache.get("foo:org/foo")).thenReturn(packageSnapshot); + when(resourceCache.get("foo")).thenReturn(project); + when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(aFile); + when(resourceCache.get("foo:org/foo")).thenReturn(aDirectory); - measurePersister = new MeasurePersister(getMyBatis(), resourcePersister, ruleFinder, memoryOptimizer); + measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache); } @Test @@ -86,11 +90,10 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { setupData("empty"); Measure measure = new Measure(ncloc()).setValue(1234.0); - measurePersister.saveMeasure(project, measure); + when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo", "ncloc"}, measure))); + measurePersister.persist(); checkTables("shouldInsertMeasure", "project_measures"); - verify(memoryOptimizer).evictDataMeasure(eq(measure), any(MeasureModel.class)); - assertThat(measure.getId()).isNotNull(); } @Test @@ -98,20 +101,12 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { setupData("empty"); Measure measure = new Measure(ncloc()).setValue(1234.0).setAlertText(TOO_LONG); + when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo", "ncloc"}, measure))); thrown.expect(SonarException.class); thrown.expectMessage("Unable to save measure for metric [ncloc] on component [foo]"); - measurePersister.saveMeasure(project, measure); - } - - @Test - public void should_reload_measure() { - Measure measure = new Measure(ncloc()); - - measurePersister.reloadMeasure(measure); - - verify(memoryOptimizer).reloadMeasure(measure); + measurePersister.persist(); } @Test @@ -122,10 +117,11 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { when(ruleFinder.findByKey("pmd", "key")).thenReturn(rule); Measure measure = new RuleMeasure(ncloc(), rule, RulePriority.MAJOR, 1).setValue(1234.0); - measurePersister.saveMeasure(project, measure); + when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo", "ncloc"}, measure))); + + measurePersister.persist(); checkTables("shouldInsertRuleMeasure", "project_measures"); - assertThat(measure.getId()).isNotNull(); } @Test @@ -133,21 +129,21 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { setupData("empty"); Measure withLargeData = new Measure(ncloc()).setData(LONG); - measurePersister.saveMeasure(project, withLargeData); + when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo", "ncloc"}, withLargeData))); - checkTables("shouldInsertMeasureWithLargeData", "project_measures", "measure_data"); + measurePersister.persist(); - ArgumentCaptor validMeasureModel = ArgumentCaptor.forClass(MeasureModel.class); - verify(memoryOptimizer).evictDataMeasure(eq(withLargeData), validMeasureModel.capture()); - assertThat(validMeasureModel.getValue().getMeasureData().getId()).isNotNull(); - assertThat(withLargeData.getId()).isNotNull(); + checkTables("shouldInsertMeasureWithLargeData", "project_measures", "measure_data"); } @Test public void should_not_save_best_values() { setupData("empty"); - measurePersister.saveMeasure(aFile, new Measure(coverage()).setValue(100.0)); + Measure measure = new Measure(coverage()).setValue(100.0); + when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo:org/foo/Bar.java", "coverage"}, measure))); + + measurePersister.persist(); assertEmptyTables("project_measures", "measure_data"); } @@ -156,7 +152,10 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { public void should_not_save_memory_only_measures() { setupData("empty"); - measurePersister.saveMeasure(aFile, new Measure("ncloc").setPersistenceMode(PersistenceMode.MEMORY)); + Measure measure = new Measure("ncloc").setPersistenceMode(PersistenceMode.MEMORY); + when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo:org/foo/Bar.java", "ncloc"}, measure))); + + measurePersister.persist(); assertEmptyTables("project_measures", "measure_data"); } @@ -165,88 +164,15 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { public void should_always_save_non_file_measures() { setupData("empty"); - measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(200.0)); - measurePersister.saveMeasure(aDirectory, new Measure(ncloc()).setValue(300.0)); - - checkTables("shouldAlwaysPersistNonFileMeasures", "project_measures"); - } - - @Test - public void should_update_measure() { - setupData("data"); - - measurePersister.saveMeasure(project, new Measure(coverage()).setValue(12.5).setId(1L)); - measurePersister.saveMeasure(project, new Measure(coverage()).setData(SHORT).setId(2L)); - measurePersister.saveMeasure(aDirectory, new Measure(coverage()).setData(LONG).setId(3L)); - - checkTables("shouldUpdateMeasure", "project_measures", "measure_data"); - } - - @Test - public void should_add_delayed_measure_several_times() { - setupData("empty"); - - Measure measure = new Measure(ncloc()); - - measurePersister.setDelayedMode(true); - measurePersister.saveMeasure(project, measure.setValue(200.0)); - measurePersister.saveMeasure(project, measure.setValue(300.0)); - measurePersister.dump(); - - checkTables("shouldAddDelayedMeasureSeveralTimes", "project_measures"); - } - - @Test - public void should_delay_saving() { - setupData("empty"); - - measurePersister.setDelayedMode(true); - measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setData(SHORT)); - measurePersister.saveMeasure(aDirectory, new Measure(ncloc()).setValue(50.0).setData(LONG)); - - assertEmptyTables("project_measures"); - - measurePersister.dump(); - checkTables("shouldDelaySaving", "project_measures", "measure_data"); - } - - @Test - public void should_display_contextual_info_when_error_during_delay_saving() { - setupData("empty"); - - measurePersister.setDelayedMode(true); - - measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setData(SHORT).setAlertText(TOO_LONG)); - - thrown.expect(SonarException.class); - thrown.expectMessage("Unable to save measure for metric [ncloc] on component [foo]"); - - measurePersister.dump(); - } - - @Test - public void should_not_delay_saving_with_database_only_measure() { - setupData("empty"); - - measurePersister.setDelayedMode(true); - measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setPersistenceMode(PersistenceMode.DATABASE)); - measurePersister.saveMeasure(aDirectory, new Measure(ncloc()).setValue(50.0)); + Measure measure1 = new Measure(ncloc()).setValue(200.0); + Measure measure2 = new Measure(ncloc()).setValue(300.0); + when(measureCache.entries()).thenReturn(Arrays.asList( + new Cache.Entry(new String[] {"foo", "ncloc"}, measure1), + new Cache.Entry(new String[] {"foo:org/foo", "ncloc"}, measure2))); - checkTables("shouldInsertMeasure", "project_measures"); - } + measurePersister.persist(); - @Test - public void should_not_save_best_value_measures_in_delayed_mode() { - setupData("empty"); - - measurePersister.setDelayedMode(true); - measurePersister.saveMeasure(aFile, new Measure(coverage()).setValue(100.0)); - - assertEmptyTables("project_measures", "measure_data"); - - measurePersister.dump(); - - assertEmptyTables("project_measures", "measure_data"); + checkTables("shouldAlwaysPersistNonFileMeasures", "project_measures"); } @Test diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java deleted file mode 100644 index 468654da72c..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java +++ /dev/null @@ -1,94 +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.index; - -import org.junit.Test; -import org.sonar.api.database.model.MeasureData; -import org.sonar.api.database.model.MeasureModel; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.PersistenceMode; -import org.sonar.jpa.test.AbstractDbUnitTestCase; - -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.core.IsNull.nullValue; -import static org.junit.Assert.assertThat; - -public class MemoryOptimizerTest extends AbstractDbUnitTestCase { - - @Test - public void shouldEvictDatabaseOnlyMeasure() { - MemoryOptimizer optimizer = new MemoryOptimizer(getSession()); - Measure measure = new Measure(CoreMetrics.CONDITIONS_BY_LINE) - .setData("10=23") - .setPersistenceMode(PersistenceMode.DATABASE) - .setId(12345L); - MeasureModel model = newPersistedModel(); - - optimizer.evictDataMeasure(measure, model); - - assertThat(optimizer.isTracked(12345L),is(true)); - assertThat(measure.getData(), nullValue());// data has been removed from memory - } - - @Test - public void shouldNotEvictStandardMeasure() { - MemoryOptimizer optimizer = new MemoryOptimizer(getSession()); - Measure measure = new Measure(CoreMetrics.PROFILE) - .setData("Sonar way") - .setId(12345L); - MeasureModel model = newPersistedModel(); - - optimizer.evictDataMeasure(measure, model); - - assertThat(optimizer.isTracked(12345L),is(false)); - assertThat(measure.getData(), is("Sonar way")); - } - - @Test - public void shouldReloadEvictedMeasure() { - setupData("shouldReloadEvictedMeasure"); - MemoryOptimizer optimizer = new MemoryOptimizer(getSession()); - Measure measure = new Measure(CoreMetrics.CONDITIONS_BY_LINE) - .setData("initial") - .setPersistenceMode(PersistenceMode.DATABASE) - .setId(12345L); - - optimizer.evictDataMeasure(measure, newPersistedModel()); - assertThat(measure.getData(), nullValue()); - - optimizer.reloadMeasure(measure); - - assertThat(measure.getData().length(), greaterThan(5)); - - optimizer.flushMemory(); - assertThat(measure.getData(), nullValue()); - } - - private MeasureModel newPersistedModel() { - MeasureModel model = new MeasureModel(); - model.setId(12345L); - MeasureData measureData = new MeasureData(); - measureData.setId(500); - model.setMeasureData(measureData); - return model; - } -} diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java new file mode 100644 index 00000000000..a1791ff676d --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java @@ -0,0 +1,83 @@ +/* + * 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.scan.measure; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.RuleMeasure; +import org.sonar.api.resources.Project; +import org.sonar.api.rules.RulePriority; +import org.sonar.batch.index.Caches; +import org.sonar.batch.index.CachesTest; + +import static org.fest.assertions.Assertions.assertThat; + +public class MeasureCacheTest { + + @Rule + public TemporaryFolder temp = new TemporaryFolder(); + + Caches caches; + + @Before + public void start() throws Exception { + caches = CachesTest.createCacheOnTemp(temp); + caches.start(); + } + + @After + public void stop() { + caches.stop(); + } + + @Test + public void should_add_measure() throws Exception { + MeasureCache cache = new MeasureCache(caches); + Project p = new Project("struts"); + + assertThat(cache.entries()).hasSize(0); + + assertThat(cache.byResource(p)).hasSize(0); + assertThat(cache.byMetric(p, "ncloc")).hasSize(0); + + Measure m = new Measure(CoreMetrics.NCLOC, 1.0); + cache.put(p, m); + + assertThat(cache.entries()).hasSize(1); + + assertThat(cache.byMetric(p, "ncloc")).hasSize(1); + assertThat(cache.byMetric(p, "ncloc").iterator().next()).isEqualTo(m); + assertThat(cache.byResource(p)).hasSize(1); + assertThat(cache.byResource(p).iterator().next()).isEqualTo(m); + + Measure mRule = RuleMeasure.createForPriority(CoreMetrics.CRITICAL_VIOLATIONS, RulePriority.BLOCKER, 1.0); + cache.put(p, mRule); + + assertThat(cache.entries()).hasSize(2); + + assertThat(cache.byResource(p)).hasSize(2); + assertThat(cache.byMetric(p, "ncloc")).hasSize(1); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java index 866785feeef..4eb61021ca5 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java @@ -30,6 +30,8 @@ import org.sonar.api.rules.Violation; import org.sonar.api.violations.ViolationQuery; import org.sonar.graph.DirectedGraphAccessor; +import javax.annotation.CheckForNull; + import java.util.Collection; import java.util.Date; import java.util.List; @@ -118,8 +120,10 @@ public abstract class SonarIndex implements DirectedGraphAccessor M getMeasures(Resource resource, MeasuresFilter filter); /** diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java index e2448adb5da..5aa343120b6 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java @@ -28,6 +28,7 @@ import org.sonar.api.technicaldebt.batch.Requirement; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import java.io.Serializable; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Date; @@ -37,7 +38,7 @@ import java.util.Date; * * @since 1.10 */ -public class Measure { +public class Measure implements Serializable { private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5"; protected static final int MAX_TEXT_SIZE = 96; @@ -47,8 +48,6 @@ public class Measure { */ public static final int DEFAULT_PRECISION = 1; - // for internal use - private Long id; protected String metricKey; protected Metric metric; protected Double value; @@ -412,24 +411,6 @@ public class Measure { return this; } - /** - * @return the measure id - Internal use only - */ - public Long getId() { - return id; - } - - /** - * Sets the measure id - Internal use only - * - * @param id the id - * @return the measure object instance - */ - public Measure setId(Long id) { - this.id = id; - return this; - } - /** * @return the first variation value * @since 2.5 @@ -646,7 +627,7 @@ public class Measure { return metric.isOptimizedBestValue() == Boolean.TRUE && metric.getBestValue() != null && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0) - && allNull(id, alertStatus, description, tendency, url, data) + && allNull(alertStatus, description, tendency, url, data) && isZeroVariation(variation1, variation2, variation3, variation4, variation5); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java index e6c0c4912e1..c7731a1120a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java @@ -19,13 +19,12 @@ */ package org.sonar.api.measures; -import java.util.Collection; /** * @since 1.10 */ public interface MeasuresFilter { - M filter(Collection measures); + M filter(Iterable measures); } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java index 4ed4a8ffb1b..7a89e5f2030 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java @@ -19,6 +19,7 @@ */ package org.sonar.api.measures; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.technicaldebt.batch.Characteristic; import org.sonar.api.technicaldebt.batch.Requirement; @@ -37,8 +38,15 @@ public final class MeasuresFilters { public static MeasuresFilter> all() { return new MeasuresFilter>() { - public Collection filter(Collection measures) { - return measures; + @Override + public Collection filter(Iterable measures) { + Collection all = new ArrayList(); + for (Measure measure : measures) { + if (measure != null) { + all.add(measure); + } + } + return all; } }; } @@ -49,8 +57,8 @@ public final class MeasuresFilters { public static MeasuresFilter metric(final String metricKey) { return new MetricFilter(metricKey) { - - public Measure filter(Collection measures) { + @Override + public Measure filter(Iterable measures) { if (measures == null) { return null; } @@ -70,7 +78,8 @@ public final class MeasuresFilters { public static MeasuresFilter characteristic(final Metric metric, final Characteristic characteristic) { return new MetricFilter(metric) { - public Measure filter(Collection measures) { + @Override + public Measure filter(Iterable measures) { if (measures == null) { return null; } @@ -100,7 +109,8 @@ public final class MeasuresFilters { public static MeasuresFilter requirement(final Metric metric, final Requirement requirement) { return new MetricFilter(metric) { - public Measure filter(Collection measures) { + @Override + public Measure filter(Iterable measures) { if (measures == null) { return null; } @@ -128,7 +138,7 @@ public final class MeasuresFilters { */ public static MeasuresFilter measure(final Measure measure) { return new MeasuresFilter() { - public Measure filter(Collection measures) { + public Measure filter(Iterable measures) { if (measures == null) { return null; } @@ -143,7 +153,7 @@ public final class MeasuresFilters { } public static MeasuresFilter rule(final Metric metric, final Rule rule) { - return new RuleFilter(metric, rule); + return new RuleFilter(metric, rule.ruleKey()); } public static MeasuresFilter> rules(final Metric metric) { @@ -151,10 +161,11 @@ public final class MeasuresFilters { private boolean apply(Measure measure) { return measure instanceof RuleMeasure && metric.equals(measure.getMetric()) - && measure.getPersonId() == null && ((RuleMeasure) measure).getRule() != null; + && measure.getPersonId() == null && ((RuleMeasure) measure).ruleKey() != null; } - public Collection filter(Collection measures) { + @Override + public Collection filter(Iterable measures) { if (measures == null) { return null; } @@ -202,7 +213,8 @@ public final class MeasuresFilters { abstract boolean doApply(RuleMeasure ruleMeasure); - public M filter(Collection measures) { + @Override + public M filter(Iterable measures) { if (measures == null) { return null; } @@ -216,17 +228,17 @@ public final class MeasuresFilters { } private static class RuleFilter extends AbstractRuleMeasureFilter { - private Rule rule; + private RuleKey ruleKey; - protected RuleFilter(Metric metric, Rule rule) { + protected RuleFilter(Metric metric, RuleKey ruleKey) { super(metric); - this.rule = rule; + this.ruleKey = ruleKey; } @Override boolean doApply(RuleMeasure measure) { - return measure.getRule() != null - && rule.equals(measure.getRule()); + return measure.ruleKey() != null + && ruleKey.equals(measure.ruleKey()); } } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java index dc7d507153b..bc809c88884 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java @@ -27,7 +27,16 @@ import org.sonar.api.ServerExtension; import org.sonar.api.batch.InstantiationStrategy; import javax.annotation.Nullable; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.Transient; + +import java.io.Serializable; /** * This class represents the definition of a metric in Sonar. @@ -37,7 +46,7 @@ import javax.persistence.*; @Table(name = "metrics") @Entity(name = "Metric") @InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class Metric implements ServerExtension, BatchExtension { +public class Metric implements ServerExtension, BatchExtension, Serializable { /** * A metric bigger value means a degradation @@ -80,7 +89,7 @@ public class Metric implements ServerExtension, BatchExtension { private Integer id; @Transient - private Formula formula; + private transient Formula formula; @Column(name = "name", updatable = false, nullable = false, length = 64) private String key; @@ -206,7 +215,7 @@ public class Metric implements ServerExtension, BatchExtension { */ @Deprecated public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, @Nullable String domain, - boolean userManaged) { + boolean userManaged) { this.key = key; this.description = description; this.type = type; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java index b0e1199a5c1..b2feae04744 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java @@ -22,6 +22,7 @@ package org.sonar.api.measures; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; +import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RulePriority; @@ -32,24 +33,54 @@ import javax.annotation.Nullable; */ public class RuleMeasure extends Measure { - private Rule rule; + /** + * @deprecated since 4.4 + */ + @Deprecated + private transient Rule rule; + private RuleKey ruleKey; private RulePriority rulePriority; /** * This constructor is for internal use only. Please use static methods createForXXX(). + * @deprecated since 4.4 use {@link #RuleMeasure(Metric, RuleKey, RulePriority, Integer)} */ + @Deprecated public RuleMeasure(Metric metric, @Nullable Rule rule, @Nullable RulePriority rulePriority, @Nullable Integer ruleCategory) { - super(metric); + this(metric, rule.ruleKey(), rulePriority, ruleCategory); this.rule = rule; + } + + public RuleMeasure(Metric metric, @Nullable RuleKey ruleKey, @Nullable RulePriority rulePriority, @Nullable Integer ruleCategory) { + super(metric); + this.ruleKey = ruleKey; this.rulePriority = rulePriority; } + public RuleKey ruleKey() { + return ruleKey; + } + + public RuleMeasure setRuleKey(RuleKey ruleKey) { + this.ruleKey = ruleKey; + return this; + } + + /** + * @deprecated since 4.4 use {@link #ruleKey()} + */ + @Deprecated public Rule getRule() { return rule; } + /** + * @deprecated since 4.4 use {@link #setRuleKey()} + */ + @Deprecated public RuleMeasure setRule(Rule rule) { this.rule = rule; + this.ruleKey = rule.ruleKey(); return this; } @@ -115,10 +146,10 @@ public class RuleMeasure extends Measure { } RuleMeasure other = (RuleMeasure) obj; return new EqualsBuilder() - .append(getMetric(), other.getMetric()) - .append(personId, other.personId) - .append(rule, other.rule) - .isEquals(); + .append(getMetric(), other.getMetric()) + .append(personId, other.personId) + .append(ruleKey, other.ruleKey) + .isEquals(); } @Override @@ -129,35 +160,42 @@ public class RuleMeasure extends Measure { @Override public int hashCode() { return new HashCodeBuilder(17, 37) - .append(getMetric()) - .append(personId) - .append(rule) - .toHashCode(); + .append(getMetric()) + .append(personId) + .append(ruleKey) + .toHashCode(); } @Override public String toString() { return new ToStringBuilder(this) - .append("id", getId()) - .append("metric", metric) - .append("personId", personId) - .append("rule", rule) - .append("value", value) - .append("data", data) - .append("description", description) - .append("alertStatus", alertStatus) - .append("alertText", alertText) - .append("tendency", tendency) - .append("severity", rulePriority) - .toString(); + .append("metric", metric) + .append("personId", personId) + .append("ruleKey", ruleKey) + .append("value", value) + .append("data", data) + .append("description", description) + .append("alertStatus", alertStatus) + .append("alertText", alertText) + .append("tendency", tendency) + .append("severity", rulePriority) + .toString(); } + /** + * @deprecated since 4.4 use {@link #createForRule(Metric, RuleKey, Double)} + */ + @Deprecated public static RuleMeasure createForRule(Metric metric, Rule rule, @Nullable Double value) { return new RuleMeasure(metric, rule, null, null).setValue(value); } + public static RuleMeasure createForRule(Metric metric, RuleKey ruleKey, @Nullable Double value) { + return new RuleMeasure(metric, ruleKey, null, null).setValue(value); + } + public static RuleMeasure createForPriority(Metric metric, RulePriority priority, @Nullable Double value) { - return new RuleMeasure(metric, null, priority, null).setValue(value); + return new RuleMeasure(metric, (RuleKey) null, priority, null).setValue(value); } /** @@ -165,6 +203,6 @@ public class RuleMeasure extends Measure { */ @Deprecated public static RuleMeasure createForCategory(Metric metric, Integer category, @Nullable Double value) { - return new RuleMeasure(metric, null, null, category).setValue(value); + return new RuleMeasure(metric, (RuleKey) null, null, category).setValue(value); } } diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java index 39453f963ff..402a5ec9f9d 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java @@ -34,7 +34,19 @@ import org.sonar.check.Cardinality; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; import java.util.ArrayList; import java.util.Date; @@ -279,15 +291,15 @@ public class Rule { public RuleParam createParameter() { RuleParam parameter = new RuleParam() - .setRule(this); + .setRule(this); params.add(parameter); return parameter; } public RuleParam createParameter(String key) { RuleParam parameter = new RuleParam() - .setKey(key) - .setRule(this); + .setKey(key) + .setRule(this); params.add(parameter); return parameter; } @@ -468,7 +480,6 @@ public class Rule { return this; } - /** * For internal use only. * @@ -499,34 +510,34 @@ public class Rule { } Rule other = (Rule) obj; return new EqualsBuilder() - .append(pluginName, other.getRepositoryKey()) - .append(key, other.getKey()) - .isEquals(); + .append(pluginName, other.getRepositoryKey()) + .append(key, other.getKey()) + .isEquals(); } @Override public int hashCode() { return new HashCodeBuilder(17, 37) - .append(pluginName) - .append(key) - .toHashCode(); + .append(pluginName) + .append(key) + .toHashCode(); } @Override public String toString() { // Note that ReflectionToStringBuilder will not work here - see SONAR-3077 return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) - .append("id", id) - .append("name", name) - .append("key", key) - .append("configKey", configKey) - .append("plugin", pluginName) - .append("severity", priority) - .append("cardinality", cardinality) - .append("status", status) - .append("language", language) - .append("parent", parent) - .toString(); + .append("id", id) + .append("name", name) + .append("key", key) + .append("configKey", configKey) + .append("plugin", pluginName) + .append("severity", priority) + .append("cardinality", cardinality) + .append("status", status) + .append("language", language) + .append("parent", parent) + .toString(); } @CheckForNull diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java index fadad455575..b649a4e466a 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java @@ -22,6 +22,7 @@ package org.sonar.api.technicaldebt.batch; import javax.annotation.CheckForNull; +import java.io.Serializable; import java.util.Date; import java.util.List; @@ -30,7 +31,7 @@ import java.util.List; * @deprecated since 4.3 */ @Deprecated -public interface Characteristic { +public interface Characteristic extends Serializable { Integer id(); diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Requirement.java b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Requirement.java index 606d2a3fb18..71204e9ffb8 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Requirement.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Requirement.java @@ -24,6 +24,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.WorkUnit; import org.sonar.api.utils.internal.WorkDuration; +import java.io.Serializable; import java.util.Date; /** @@ -31,7 +32,7 @@ import java.util.Date; * @deprecated since 4.3 */ @Deprecated -public interface Requirement { +public interface Requirement extends Serializable { Integer id(); diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasuresFiltersTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasuresFiltersTest.java index f9f569f199b..ea075ed2a99 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasuresFiltersTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasuresFiltersTest.java @@ -25,6 +25,7 @@ import org.sonar.api.rules.RulePriority; import java.util.Arrays; import java.util.Collection; +import java.util.Iterator; import java.util.List; import static org.hamcrest.core.Is.is; @@ -37,8 +38,8 @@ public class MeasuresFiltersTest { MeasuresFilter filter = MeasuresFilters.metric(CoreMetrics.VIOLATIONS); Collection measures = Arrays.asList( - RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 50.0), - new Measure(CoreMetrics.VIOLATIONS, 500.0)); + RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 50.0), + new Measure(CoreMetrics.VIOLATIONS, 500.0)); assertThat(filter.filter(measures).getValue(), is(500.0)); } @@ -46,10 +47,13 @@ public class MeasuresFiltersTest { @Test public void all() { Collection measures = Arrays.asList( - RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 50.0), - new Measure(CoreMetrics.VIOLATIONS, 500.0)); + RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 50.0), + new Measure(CoreMetrics.VIOLATIONS, 500.0)); - assertThat(MeasuresFilters.all().filter(measures).size(), is(2)); + Iterator filteredMeasures = MeasuresFilters.all().filter(measures).iterator(); + filteredMeasures.next(); + filteredMeasures.next(); + assertThat(filteredMeasures.hasNext(), is(false)); } @Test @@ -58,13 +62,13 @@ public class MeasuresFiltersTest { Rule rule2 = new Rule("pmd", "key2"); MeasuresFilter filter = MeasuresFilters.rule(CoreMetrics.VIOLATIONS, rule1); List measures = Arrays.asList( - RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule1, 50.0), - RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule2, 10.0), - RuleMeasure.createForRule(CoreMetrics.VIOLATIONS_DENSITY, rule2, 3.3), + RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule1, 50.0), + RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule2, 10.0), + RuleMeasure.createForRule(CoreMetrics.VIOLATIONS_DENSITY, rule2, 3.3), - RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 400.0), - RuleMeasure.createForPriority(CoreMetrics.COVERAGE, RulePriority.CRITICAL, 400.0), - new Measure(CoreMetrics.VIOLATIONS, 500.0)); + RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 400.0), + RuleMeasure.createForPriority(CoreMetrics.COVERAGE, RulePriority.CRITICAL, 400.0), + new Measure(CoreMetrics.VIOLATIONS, 500.0)); assertThat(filter.filter(measures).getValue(), is(50.0)); } @@ -75,13 +79,13 @@ public class MeasuresFiltersTest { Rule rule2 = new Rule("pmd", "key2"); MeasuresFilter> filter = MeasuresFilters.rules(CoreMetrics.VIOLATIONS); List measures = Arrays.asList( - RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule1, 50.0), - RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule2, 10.0), - RuleMeasure.createForRule(CoreMetrics.VIOLATIONS_DENSITY, rule2, 3.3), + RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule1, 50.0), + RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule2, 10.0), + RuleMeasure.createForRule(CoreMetrics.VIOLATIONS_DENSITY, rule2, 3.3), - RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 400.0), - RuleMeasure.createForPriority(CoreMetrics.COVERAGE, RulePriority.CRITICAL, 400.0), - new Measure(CoreMetrics.VIOLATIONS, 500.0)); + RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, RulePriority.CRITICAL, 400.0), + RuleMeasure.createForPriority(CoreMetrics.COVERAGE, RulePriority.CRITICAL, 400.0), + new Measure(CoreMetrics.VIOLATIONS, 500.0)); assertThat(filter.filter(measures).size(), is(2)); } @@ -90,10 +94,10 @@ public class MeasuresFiltersTest { public void measure() { MeasuresFilter filter = MeasuresFilters.measure(new Measure(CoreMetrics.VIOLATIONS)); List measures = Arrays.asList( - new Measure(CoreMetrics.COMMENT_LINES, 50.0), - new Measure(CoreMetrics.VIOLATIONS, 10.0), - RuleMeasure.createForCategory(CoreMetrics.VIOLATIONS, 2, 12.0), - new Measure(CoreMetrics.COVERAGE, 15.0)); + new Measure(CoreMetrics.COMMENT_LINES, 50.0), + new Measure(CoreMetrics.VIOLATIONS, 10.0), + RuleMeasure.createForCategory(CoreMetrics.VIOLATIONS, 2, 12.0), + new Measure(CoreMetrics.COVERAGE, 15.0)); assertThat(filter.filter(measures).getValue(), is(10.0)); } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java b/sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java index 091d8c781cd..3f9f67ce236 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java @@ -46,7 +46,7 @@ public class IsRuleMeasure extends ArgumentMatcher { } RuleMeasure m = (RuleMeasure) o; return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(rule, m.getRule()) && + ObjectUtils.equals(rule.ruleKey(), m.ruleKey()) && NumberUtils.compare(value, m.getValue()) == 0; } } -- 2.39.5