From 7b301db8c7e3000ff2a04fce5534e45e7d973a1f Mon Sep 17 00:00:00 2001 From: Julien HENRY Date: Fri, 25 Apr 2014 11:17:09 +0200 Subject: [PATCH] Revert "SONAR-3437, SONAR-5189 Store measures in a persistit cache" This reverts commit 46395126b184c343df5471a147cdb48133f59f6f. --- .../issue/CountUnresolvedIssuesDecorator.java | 79 +++---- .../core/timemachine/TendencyDecorator.java | 18 +- .../core/timemachine/VariationDecorator.java | 34 +-- .../CountUnresolvedIssuesDecoratorTest.java | 21 +- .../timemachine/TendencyDecoratorTest.java | 7 +- .../timemachine/VariationDecoratorTest.java | 27 +-- .../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 | 17 +- .../org/sonar/batch/index/DefaultIndex.java | 49 ++-- .../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 +- .../qualitygate/QualityGateVerifier.java | 27 +-- .../batch/scan/ProjectScanContainer.java | 33 +-- .../batch/scan/measure/MeasureCache.java | 82 ------- .../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 | 158 +++++++++---- .../batch/index/MemoryOptimizerTest.java | 94 ++++++++ .../qualitygate/QualityGateVerifierTest.java | 60 ++--- .../batch/scan/measure/MeasureCacheTest.java | 155 ------------- .../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 +- 38 files changed, 875 insertions(+), 790 deletions(-) create mode 100644 sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java delete mode 100644 sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java delete mode 100644 sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java delete 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 00c145353dd..05ad24edf1c 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,32 +20,16 @@ package org.sonar.plugins.core.issue; import com.google.common.annotations.VisibleForTesting; -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 com.google.common.collect.*; import org.apache.commons.lang.time.DateUtils; -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.batch.*; import org.sonar.api.component.ResourcePerspectives; import org.sonar.api.issue.Issuable; import org.sonar.api.issue.Issue; -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.measures.*; 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; @@ -54,12 +38,7 @@ import org.sonar.batch.components.TimeMachineConfiguration; import javax.annotation.Nullable; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * Computes metrics related to number of issues. @@ -101,7 +80,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator { CoreMetrics.OPEN_ISSUES, CoreMetrics.REOPENED_ISSUES, CoreMetrics.CONFIRMED_ISSUES - ); + ); } public void decorate(Resource resource, DecoratorContext context) { @@ -111,7 +90,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; @@ -119,8 +98,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(issue.ruleKey()); + Multiset rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity())); + rulesBag.add(rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule())); issuesPerSeverity.put(RulePriority.valueOf(issue.severity()), issue); if (Issue.STATUS_OPEN.equals(issue.status())) { @@ -180,22 +159,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; - RuleKey ruleKey = childRuleMeasure.ruleKey(); - if (ruleKey != null && MeasureUtils.hasValue(childRuleMeasure)) { - Multiset rulesBag = initRules(rulesPerSeverity, severity); - rulesBag.add(ruleKey, childRuleMeasure.getIntValue()); + Rule rule = childRuleMeasure.getRule(); + if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) { + Multiset rulesBag = initRules(rulesPerSeverity, severity); + rulesBag.add(rule, 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); @@ -206,34 +185,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 childMeasuresPerRuleKeys = ArrayListMultimap.create(); - ListMultimap issuesPerRuleKeys = ArrayListMultimap.create(); - Set ruleKeys = Sets.newHashSet(); + ListMultimap childMeasuresPerRule = ArrayListMultimap.create(); + ListMultimap issuesPerRule = ArrayListMultimap.create(); + Set rules = Sets.newHashSet(); Collection children = context.getChildrenMeasures(MeasuresFilters.rules(metric)); for (Measure child : children) { RuleMeasure childRuleMeasure = (RuleMeasure) child; - RuleKey ruleKey = childRuleMeasure.ruleKey(); - if (ruleKey != null) { - childMeasuresPerRuleKeys.put(ruleKey, childRuleMeasure); - ruleKeys.add(ruleKey); + Rule rule = childRuleMeasure.getRule(); + if (rule != null) { + childMeasuresPerRule.put(rule, childRuleMeasure); + rules.add(rule); } } for (Issue issue : issues) { if (RulePriority.valueOf(issue.severity()).equals(severity)) { - ruleKeys.add(issue.ruleKey()); - issuesPerRuleKeys.put(issue.ruleKey(), issue); + Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule()); + rules.add(rule); + issuesPerRule.put(rule, issue); } } - for (RuleKey ruleKey : ruleKeys) { - Rule rule = rulefinder.findByKey(ruleKey); + for (Rule rule : rules) { 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, childMeasuresPerRuleKeys.get(rule.ruleKey())) + countIssues(issuesPerRuleKeys.get(rule.ruleKey()), period); + double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + countIssues(issuesPerRule.get(rule), period); measure.setVariation(variationIndex, sum); } context.saveMeasure(measure); @@ -263,8 +242,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/TendencyDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyDecorator.java index 1e20c12f994..c824537cece 100644 --- a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyDecorator.java +++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyDecorator.java @@ -24,13 +24,7 @@ import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DateUtils; -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.batch.TimeMachine; -import org.sonar.api.batch.TimeMachineQuery; +import org.sonar.api.batch.*; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; import org.sonar.api.measures.MetricFinder; @@ -38,7 +32,6 @@ import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.Scopes; import org.sonar.batch.components.PeriodsDefinition; -import org.sonar.batch.index.DefaultIndex; import org.sonar.core.DryRunIncompatible; import java.util.List; @@ -53,11 +46,9 @@ public class TendencyDecorator implements Decorator { private TimeMachineQuery query; private TendencyAnalyser analyser; private List metrics; - private final DefaultIndex index; - public TendencyDecorator(TimeMachine timeMachine, MetricFinder metricFinder, DefaultIndex index) { + public TendencyDecorator(TimeMachine timeMachine, MetricFinder metricFinder) { this.timeMachine = timeMachine; - this.index = index; this.analyser = new TendencyAnalyser(); this.metrics = Lists.newLinkedList(); for (Metric metric : metricFinder.findAll()) { @@ -67,11 +58,10 @@ public class TendencyDecorator implements Decorator { } } - TendencyDecorator(TimeMachine timeMachine, TimeMachineQuery query, TendencyAnalyser analyser, DefaultIndex index) { + TendencyDecorator(TimeMachine timeMachine, TimeMachineQuery query, TendencyAnalyser analyser) { this.timeMachine = timeMachine; this.query = query; this.analyser = analyser; - this.index = index; } @DependsUpon @@ -118,7 +108,7 @@ public class TendencyDecorator implements Decorator { values.add(measure.getValue()); measure.setTendency(analyser.analyseLevel(valuesPerMetric.get(metric))); - index.updateMeasure(resource, measure); + context.saveMeasure(measure); } } } 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 be6d1a8f35e..efebe9d7a62 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,26 +21,16 @@ package org.sonar.plugins.core.timemachine; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; -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.batch.*; +import org.sonar.api.measures.*; 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; import org.sonar.batch.components.TimeMachineConfiguration; -import org.sonar.batch.index.DefaultIndex; import java.util.Collection; import java.util.List; @@ -52,20 +42,16 @@ public class VariationDecorator implements Decorator { private List projectPastSnapshots; private MetricFinder metricFinder; private PastMeasuresLoader pastMeasuresLoader; - private RuleFinder ruleFinder; - private final DefaultIndex sonarIndex; - public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration timeMachineConfiguration, RuleFinder ruleFinder, - DefaultIndex index) { - this(pastMeasuresLoader, metricFinder, timeMachineConfiguration.getProjectPastSnapshots(), ruleFinder, index); + + public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration timeMachineConfiguration) { + this(pastMeasuresLoader, metricFinder, timeMachineConfiguration.getProjectPastSnapshots()); } - VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List projectPastSnapshots, RuleFinder ruleFinder, DefaultIndex index) { + VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List projectPastSnapshots) { this.pastMeasuresLoader = pastMeasuresLoader; this.projectPastSnapshots = projectPastSnapshots; this.metricFinder = metricFinder; - this.ruleFinder = ruleFinder; - this.sonarIndex = index; } public boolean shouldExecuteOnProject(Project project) { @@ -96,10 +82,10 @@ public class VariationDecorator implements Decorator { private void computeVariation(Resource resource, DecoratorContext context, PastSnapshot pastSnapshot) { List pastMeasures = pastMeasuresLoader.getPastMeasures(resource, pastSnapshot); - compareWithPastMeasures(resource, context, pastSnapshot.getIndex(), pastMeasures); + compareWithPastMeasures(context, pastSnapshot.getIndex(), pastMeasures); } - void compareWithPastMeasures(Resource resource, DecoratorContext context, int index, List pastMeasures) { + void compareWithPastMeasures(DecoratorContext context, int index, List pastMeasures) { Map pastMeasuresByKey = Maps.newHashMap(); for (Object[] pastMeasure : pastMeasures) { pastMeasuresByKey.put(new MeasureKey(pastMeasure), pastMeasure); @@ -112,11 +98,11 @@ 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 ? ruleFinder.findByKey(((RuleMeasure) measure).ruleKey()).getId() : null; + Integer ruleId = measure instanceof RuleMeasure ? ((RuleMeasure) measure).getRule().getId() : null; Object[] pastMeasure = pastMeasuresByKey.get(new MeasureKey(metricId, characteristicId, personId, ruleId)); if (updateVariation(measure, pastMeasure, index)) { - sonarIndex.updateMeasure(resource, measure); + context.saveMeasure(measure); } } } 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 600f455ca21..41241336bd0 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,11 +31,7 @@ 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.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.measures.*; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.resources.Scopes; @@ -59,12 +55,7 @@ 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.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; +import static org.mockito.Mockito.*; public class CountUnresolvedIssuesDecoratorTest { @@ -92,9 +83,9 @@ public class CountUnresolvedIssuesDecoratorTest { ruleB1 = Rule.create().setRepositoryKey("ruleB1").setKey("ruleB1").setName("nameB1"); ruleFinder = mock(RuleFinder.class); - when(ruleFinder.findByKey(ruleA1.ruleKey())).thenReturn(ruleA1); - when(ruleFinder.findByKey(ruleA2.ruleKey())).thenReturn(ruleA2); - when(ruleFinder.findByKey(ruleB1.ruleKey())).thenReturn(ruleB1); + when(ruleFinder.findByKey(ruleA1.getRepositoryKey(), ruleA1.getKey())).thenReturn(ruleA1); + when(ruleFinder.findByKey(ruleA2.getRepositoryKey(), ruleA2.getKey())).thenReturn(ruleA2); + when(ruleFinder.findByKey(ruleB1.getRepositoryKey(), ruleB1.getKey())).thenReturn(ruleB1); rightNow = new Date(); tenDaysAgo = DateUtils.addDays(rightNow, -10); @@ -333,7 +324,7 @@ public class CountUnresolvedIssuesDecoratorTest { } RuleMeasure m = (RuleMeasure) o; return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(rule.ruleKey(), m.ruleKey()) && + ObjectUtils.equals(rule, m.getRule()) && 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/TendencyDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyDecoratorTest.java index a60dfbb5e9e..8713c66537b 100644 --- a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyDecoratorTest.java +++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyDecoratorTest.java @@ -28,7 +28,6 @@ import org.sonar.api.measures.Measure; import org.sonar.api.measures.MetricFinder; import org.sonar.api.resources.Directory; import org.sonar.api.resources.Project; -import org.sonar.batch.index.DefaultIndex; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -54,7 +53,7 @@ public class TendencyDecoratorTest { MetricFinder metricFinder = mock(MetricFinder.class); when(metricFinder.findAll()).thenReturn(Arrays.asList(CoreMetrics.LINES, CoreMetrics.COVERAGE, CoreMetrics.COVERAGE_LINE_HITS_DATA, CoreMetrics.PROFILE)); - TendencyDecorator decorator = new TendencyDecorator(null, metricFinder, mock(DefaultIndex.class)); + TendencyDecorator decorator = new TendencyDecorator(null, metricFinder); TimeMachineQuery query = decorator.initQuery(project); assertThat(query.getMetrics().size(), is(2)); @@ -81,7 +80,7 @@ public class TendencyDecoratorTest { when(context.getMeasure(CoreMetrics.LINES)).thenReturn(new Measure(CoreMetrics.LINES, 1400.0)); when(context.getMeasure(CoreMetrics.COVERAGE)).thenReturn(new Measure(CoreMetrics.LINES, 90.0)); - TendencyDecorator decorator = new TendencyDecorator(timeMachine, query, analyser, mock(DefaultIndex.class)); + TendencyDecorator decorator = new TendencyDecorator(timeMachine, query, analyser); decorator.decorate(new Directory("org/foo"), context); verify(analyser).analyseLevel(Arrays.asList(1200.0, 1300.0, 1150.0, 1400.0)); @@ -100,7 +99,7 @@ public class TendencyDecoratorTest { )); DecoratorContext context = mock(DecoratorContext.class); - TendencyDecorator decorator = new TendencyDecorator(timeMachine, query, analyser, mock(DefaultIndex.class)); + TendencyDecorator decorator = new TendencyDecorator(timeMachine, query, analyser); decorator.decorate(new Directory("org/foo"), context); verify(analyser, never()).analyseLevel(anyList()); 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 971d6d9f99f..fe48596751c 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,18 +32,15 @@ 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; -import org.sonar.batch.index.DefaultIndex; import org.sonar.jpa.test.AbstractDbUnitTestCase; import java.util.Arrays; import java.util.Date; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -63,8 +60,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, mock(RuleFinder.class), - mock(DefaultIndex.class)); + VariationDecorator decorator = new VariationDecorator(mock(PastMeasuresLoader.class), mock(MetricFinder.class), timeMachineConfiguration); assertThat(decorator.shouldComputeVariation(new Project("foo"))).isTrue(); assertThat(decorator.shouldComputeVariation(new File("foo/bar.c"))).isFalse(); @@ -93,13 +89,11 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { Measure currentCoverage = newMeasure(COVERAGE, 80.0); when(context.getMeasures(Matchers.anyObject())).thenReturn(Arrays.asList(currentNcloc, currentCoverage)); - DefaultIndex index = mock(DefaultIndex.class); - VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3), mock(RuleFinder.class), - index); + VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3)); decorator.decorate(dir, context); // context updated for each variation : 2 times for ncloc and 1 time for coverage - verify(index, times(3)).updateMeasure(eq(dir), Matchers.anyObject()); + verify(context, times(3)).saveMeasure(Matchers.anyObject()); assertThat(currentNcloc.getVariation1()).isEqualTo(20.0); assertThat(currentNcloc.getVariation2()).isNull(); @@ -112,16 +106,11 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { @Test public void shouldComputeVariationOfRuleMeasures() { - RuleFinder ruleFinder = mock(RuleFinder.class); - - Rule rule1 = Rule.create("repo", "rule1"); + Rule rule1 = Rule.create(); rule1.setId(1); - Rule rule2 = Rule.create("repo", "rule2"); + Rule rule2 = Rule.create(); 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); @@ -140,13 +129,11 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase { Measure violationsRule2 = RuleMeasure.createForRule(VIOLATIONS, rule2, 70.0); when(context.getMeasures(Matchers.anyObject())).thenReturn(Arrays.asList(violations, violationsRule1, violationsRule2)); - DefaultIndex index = mock(DefaultIndex.class); - VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1), ruleFinder, - index); + VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1)); decorator.decorate(dir, context); // context updated for each variation - verify(index, times(3)).updateMeasure(eq(dir), Matchers.anyObject()); + verify(context, times(3)).saveMeasure(Matchers.anyObject()); assertThat(violations.getVariation1()).isEqualTo(20.0); } 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 565417efad0..466be91dc79 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java @@ -38,12 +38,7 @@ import org.sonar.batch.index.DefaultIndex; import javax.annotation.Nullable; import javax.persistence.Query; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; public class DefaultTimeMachine implements TimeMachine { @@ -160,6 +155,7 @@ 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 17475936308..71e9cc36a1c 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,7 +48,6 @@ 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; @@ -111,7 +110,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.ruleKey(), ruleDebts, characteristicDebts); + total += computeDebt(debt, ruleMeasure.getRule().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 ea4ed3b11a0..af6a87ae8a6 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,15 +19,24 @@ */ 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; @@ -70,7 +79,30 @@ 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); @@ -78,6 +110,16 @@ 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 39815dcfcb3..46f6bbf0a84 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,7 +26,6 @@ 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; @@ -220,6 +219,7 @@ public class Cache { } } + /** * Clears the default as well as all group caches. */ @@ -293,20 +293,6 @@ 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 */ @@ -366,6 +352,7 @@ 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 f95a5311c4e..9624513e288 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 @@ -31,7 +31,6 @@ import org.sonar.api.batch.SonarIndex; import org.sonar.api.batch.bootstrap.ProjectDefinition; import org.sonar.api.database.model.Snapshot; import org.sonar.api.design.Dependency; -import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.MeasuresFilter; import org.sonar.api.measures.MeasuresFilters; @@ -53,8 +52,6 @@ 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.qualitygate.QualityGateVerifier; -import org.sonar.batch.scan.measure.MeasureCache; import org.sonar.core.component.ComponentKeys; import org.sonar.core.component.ScanGraph; @@ -88,19 +85,17 @@ 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, MeasureCache measureCache) { + ScanGraph graph, DeprecatedViolations deprecatedViolations, ResourceKeyMigration migration) { this.persistence = persistence; this.projectTree = projectTree; this.metricFinder = metricFinder; this.graph = graph; this.deprecatedViolations = deprecatedViolations; this.migration = migration; - this.measureCache = measureCache; } public void start() { @@ -179,17 +174,29 @@ public class DefaultIndex extends SonarIndex { @Override public Measure getMeasure(Resource resource, Metric metric) { - return getMeasures(resource, MeasuresFilters.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; } @Override public M getMeasures(Resource resource, MeasuresFilter filter) { - // Reload resource so that effective key is populated - Resource indexedResource = getResource(resource); - Iterable unfiltered = measureCache.byResource(indexedResource); - return filter.filter(unfiltered); + 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); + } + return null; } + /** + * the measure is updated if it's already registered. + */ @Override public Measure addMeasure(Resource resource, Measure measure) { Bucket bucket = getBucket(resource); @@ -199,27 +206,15 @@ public class DefaultIndex extends SonarIndex { throw new SonarException("Unknown metric: " + measure.getMetricKey()); } measure.setMetric(metric); - if (measureCache.contains(resource, measure) - // Hack for SONAR-5212 - && !measure.getMetric().equals(CoreMetrics.TESTS)) { - throw new SonarException("Can not add twice the same measure on " + resource + ": " + measure); + bucket.addMeasure(measure); + + if (measure.getPersistenceMode().useDatabase()) { + persistence.saveMeasure(bucket.getResource(), measure); } - measureCache.put(resource, measure); } return measure; } - /** - * Used by some core features like TendencyDecorator, {@link QualityGateVerifier}, VariationDecorator - * that need to update some existing measures - */ - public void updateMeasure(Resource resource, Measure measure) { - if (!measureCache.contains(resource, measure)) { - throw new SonarException("Can't update measure on " + resource + ": " + measure); - } - measureCache.put(resource, 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 39b9d8d472a..14da97381cf 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,6 +22,7 @@ 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; @@ -33,14 +34,17 @@ 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, - DependencyPersister dependencyPersister, LinkPersister linkPersister, EventPersister eventPersister) { + MeasurePersister measurePersister, DependencyPersister dependencyPersister, + LinkPersister linkPersister, EventPersister eventPersister) { this.resourcePersister = resourcePersister; this.sourcePersister = sourcePersister; + this.measurePersister = measurePersister; this.dependencyPersister = dependencyPersister; this.linkPersister = linkPersister; this.eventPersister = eventPersister; @@ -51,6 +55,14 @@ 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); } @@ -70,6 +82,16 @@ 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 80cac3ab4cb..8e61a931bd0 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,69 +20,94 @@ 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; -public final class MeasurePersister implements ScanPersister { +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public final class MeasurePersister { private final MyBatis mybatis; + private final ResourcePersister resourcePersister; private final RuleFinder ruleFinder; - private final MeasureCache measureCache; - private final SnapshotCache snapshotCache; - private final ResourceCache resourceCache; + private final MemoryOptimizer memoryOptimizer; + private final SetMultimap unsavedMeasuresByResource = LinkedHashMultimap.create(); + private boolean delayedMode = false; - public MeasurePersister(MyBatis mybatis, RuleFinder ruleFinder, - MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache) { + public MeasurePersister(MyBatis mybatis, ResourcePersister resourcePersister, RuleFinder ruleFinder, MemoryOptimizer memoryOptimizer) { this.mybatis = mybatis; + this.resourcePersister = resourcePersister; this.ruleFinder = ruleFinder; - this.measureCache = measureCache; - this.snapshotCache = snapshotCache; - this.resourceCache = resourceCache; + this.memoryOptimizer = memoryOptimizer; } - @Override - public void persist() { - SqlSession session = mybatis.openSession(); - try { - MeasureMapper mapper = session.getMapper(MeasureMapper.class); + public void setDelayedMode(boolean delayedMode) { + this.delayedMode = delayedMode; + } - for (Entry entry : measureCache.entries()) { - String effectiveKey = entry.key()[0].toString(); - Measure measure = entry.value(); - Resource resource = resourceCache.get(effectiveKey); + public Measure reloadMeasure(Measure measure) { + return memoryOptimizer.reloadMeasure(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); - } - } + public void dump() { + LoggerFactory.getLogger(getClass()).debug("{} measures to dump", unsavedMeasuresByResource.size()); + + insert(getMeasuresToSave()); + } + + 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); } + 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); + } + } - session.commit(); - } finally { - MyBatis.closeQuietly(session); + 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; } + return null; + } + + private boolean shouldSaveLater(Measure measure) { + return delayedMode && measure.getPersistenceMode().useMemory(); } @VisibleForTesting @@ -100,6 +125,24 @@ public final class MeasurePersister implements ScanPersister { || 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 @@ -129,9 +172,9 @@ public final class MeasurePersister implements ScanPersister { if (measure instanceof RuleMeasure) { RuleMeasure ruleMeasure = (RuleMeasure) measure; model.setRulePriority(ruleMeasure.getSeverity()); - RuleKey ruleKey = ruleMeasure.ruleKey(); - if (ruleKey != null) { - Rule ruleWithId = ruleFinder.findByKey(ruleKey); + Rule rule = ruleMeasure.getRule(); + if (rule != null) { + Rule ruleWithId = ruleFinder.findByKey(rule.getRepositoryKey(), rule.getKey()); if (ruleWithId == null) { throw new SonarException("Can not save a measure with unknown rule " + ruleMeasure); } @@ -141,4 +184,95 @@ public final class MeasurePersister implements ScanPersister { 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 new file mode 100644 index 00000000000..9daa941b823 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java @@ -0,0 +1,117 @@ +/* + * 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 d90f63d8b66..d42e1a9f2e1 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,6 +22,7 @@ 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; @@ -31,6 +32,10 @@ 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); @@ -39,6 +44,10 @@ 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 90bafa7dc3e..0ba16ea66d7 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,6 +116,8 @@ public final class PhaseExecutor { executeInitializersPhase(); + persistenceManager.setDelayedMode(true); + if (phases.isEnabled(Phases.Phase.SENSOR)) { // Index and lock the filesystem fs.index(); @@ -133,6 +135,12 @@ 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(); @@ -154,7 +162,6 @@ 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/qualitygate/QualityGateVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java index 406ca87648c..bc77037311d 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java +++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java @@ -23,11 +23,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.commons.lang.StringUtils; -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.batch.*; import org.sonar.api.database.model.Snapshot; import org.sonar.api.i18n.I18n; import org.sonar.api.measures.CoreMetrics; @@ -38,15 +34,10 @@ import org.sonar.api.resources.Resource; import org.sonar.api.resources.ResourceUtils; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; -import org.sonar.batch.index.DefaultIndex; import org.sonar.core.qualitygate.db.QualityGateConditionDto; import org.sonar.core.timemachine.Periods; -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; +import java.util.*; public class QualityGateVerifier implements Decorator { @@ -64,15 +55,13 @@ public class QualityGateVerifier implements Decorator { private Periods periods; private I18n i18n; private Durations durations; - private final DefaultIndex index; - public QualityGateVerifier(QualityGate qualityGate, Snapshot snapshot, Periods periods, I18n i18n, Durations durations, DefaultIndex index) { + public QualityGateVerifier(QualityGate qualityGate, Snapshot snapshot, Periods periods, I18n i18n, Durations durations) { this.qualityGate = qualityGate; this.snapshot = snapshot; this.periods = periods; this.i18n = i18n; this.durations = durations; - this.index = index; } @DependedUpon @@ -88,7 +77,7 @@ public class QualityGateVerifier implements Decorator { @DependsUpon public Collection dependsUponMetrics() { Set metrics = Sets.newHashSet(); - for (ResolvedCondition condition : qualityGate.conditions()) { + for (ResolvedCondition condition: qualityGate.conditions()) { metrics.add(condition.metric()); } return metrics; @@ -102,16 +91,16 @@ public class QualityGateVerifier implements Decorator { @Override public void decorate(Resource resource, DecoratorContext context) { if (ResourceUtils.isRootProject(resource)) { - checkProjectConditions(resource, context); + checkProjectConditions(context); } } - private void checkProjectConditions(Resource resource, DecoratorContext context) { + private void checkProjectConditions(DecoratorContext context) { Metric.Level globalLevel = Metric.Level.OK; QualityGateDetails details = new QualityGateDetails(); List labels = Lists.newArrayList(); - for (ResolvedCondition condition : qualityGate.conditions()) { + for (ResolvedCondition condition: qualityGate.conditions()) { Measure measure = context.getMeasure(condition.metric()); if (measure != null) { Metric.Level level = ConditionUtils.getLevel(condition, measure); @@ -123,7 +112,7 @@ public class QualityGateVerifier implements Decorator { labels.add(text); } - index.updateMeasure(resource, measure); + context.saveMeasure(measure); if (Metric.Level.WARN == level && globalLevel != Metric.Level.ERROR) { globalLevel = Metric.Level.WARN; 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 335884d7f87..e4eacf6aa8d 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,6 +21,7 @@ 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; @@ -35,40 +36,18 @@ 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.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.bootstrap.*; import org.sonar.batch.components.PeriodsDefinition; import org.sonar.batch.debt.DebtModelProvider; import org.sonar.batch.debt.IssueChangelogDebtCalculator; -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.index.*; +import org.sonar.batch.issue.*; 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; @@ -133,6 +112,7 @@ public class ProjectScanContainer extends ComponentContainer { EventPersister.class, LinkPersister.class, MeasurePersister.class, + MemoryOptimizer.class, DefaultResourcePersister.class, SourcePersister.class, DefaultNotificationManager.class, @@ -189,9 +169,6 @@ 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 deleted file mode 100644 index b29b6c83ee0..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java +++ /dev/null @@ -1,82 +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.scan.measure; - -import com.google.common.base.Preconditions; -import org.sonar.api.BatchComponent; -import org.sonar.api.measures.Measure; -import org.sonar.api.measures.RuleMeasure; -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.values(r.getEffectiveKey()); - } - - public MeasureCache put(Resource resource, Measure measure) { - Preconditions.checkNotNull(resource.getEffectiveKey()); - Preconditions.checkNotNull(measure.getMetricKey()); - cache.put(resource.getEffectiveKey(), computeMeasureKey(measure), measure); - return this; - } - - public boolean contains(Resource resource, Measure measure) { - Preconditions.checkNotNull(resource.getEffectiveKey()); - Preconditions.checkNotNull(measure.getMetricKey()); - return cache.containsKey(resource.getEffectiveKey(), computeMeasureKey(measure)); - } - - private static String computeMeasureKey(Measure m) { - StringBuilder sb = new StringBuilder(); - if (m.getMetricKey() != null) { - sb.append(m.getMetricKey()); - } - sb.append("|"); - if (m.getCharacteristic() != null) { - sb.append(m.getCharacteristic().key()); - } - sb.append("|"); - if (m.getPersonId() != null) { - sb.append(m.getPersonId()); - } - if (m instanceof RuleMeasure) { - sb.append("|"); - sb.append(((RuleMeasure) m).ruleKey()); - } - return sb.toString(); - } -} 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 deleted file mode 100644 index 64f9c876250..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java +++ /dev/null @@ -1,27 +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. - */ - -/** - * 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 6edbbab8fbf..fc82ece746c 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.Matchers.argThat; +import static org.mockito.Mockito.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,7 +319,8 @@ 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()) + ; } } @@ -341,7 +342,8 @@ public class DebtDecoratorTest { } RuleMeasure m = (RuleMeasure) o; return ObjectUtils.equals(metric, m.getMetric()) && - ObjectUtils.equals(ruleKey, m.ruleKey()) && + ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) && + ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) && 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 cb20f15cde6..2fc521a64d5 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,9 +20,12 @@ 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; @@ -48,6 +51,42 @@ 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 9a162fbf425..4abc71694cf 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,13 +28,7 @@ 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.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.resources.*; import org.sonar.api.rules.Rule; import org.sonar.api.rules.RuleFinder; import org.sonar.api.rules.Violation; @@ -42,7 +36,6 @@ 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; @@ -75,8 +68,7 @@ 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), - mock(MeasureCache.class)); + index = new DefaultIndex(mock(PersistenceManager.class), projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, mock(ResourceKeyMigration.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 bde25e7ddc0..fc3a69628d2 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,6 +23,8 @@ 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; @@ -36,13 +38,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 { @@ -61,28 +63,22 @@ 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() { - 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); + when(resourcePersister.getSnapshotOrFail(project)).thenReturn(projectSnapshot); + when(resourcePersister.getSnapshotOrFail(aDirectory)).thenReturn(packageSnapshot); + when(resourcePersister.getSnapshot(project)).thenReturn(projectSnapshot); + when(resourcePersister.getSnapshot(aDirectory)).thenReturn(packageSnapshot); - measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache); + measurePersister = new MeasurePersister(getMyBatis(), resourcePersister, ruleFinder, memoryOptimizer); } @Test @@ -90,10 +86,11 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { setupData("empty"); Measure measure = new Measure(ncloc()).setValue(1234.0); - when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo", "ncloc"}, measure))); - measurePersister.persist(); + measurePersister.saveMeasure(project, measure); checkTables("shouldInsertMeasure", "project_measures"); + verify(memoryOptimizer).evictDataMeasure(eq(measure), any(MeasureModel.class)); + assertThat(measure.getId()).isNotNull(); } @Test @@ -101,12 +98,20 @@ 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.persist(); + measurePersister.saveMeasure(project, measure); + } + + @Test + public void should_reload_measure() { + Measure measure = new Measure(ncloc()); + + measurePersister.reloadMeasure(measure); + + verify(memoryOptimizer).reloadMeasure(measure); } @Test @@ -114,14 +119,13 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { setupData("empty"); Rule rule = Rule.create("pmd", "key"); - when(ruleFinder.findByKey(rule.ruleKey())).thenReturn(rule); + when(ruleFinder.findByKey("pmd", "key")).thenReturn(rule); Measure measure = new RuleMeasure(ncloc(), rule, RulePriority.MAJOR, 1).setValue(1234.0); - when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo", "ncloc"}, measure))); - - measurePersister.persist(); + measurePersister.saveMeasure(project, measure); checkTables("shouldInsertRuleMeasure", "project_measures"); + assertThat(measure.getId()).isNotNull(); } @Test @@ -129,21 +133,21 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { setupData("empty"); Measure withLargeData = new Measure(ncloc()).setData(LONG); - when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry(new String[] {"foo", "ncloc"}, withLargeData))); - - measurePersister.persist(); + measurePersister.saveMeasure(project, withLargeData); checkTables("shouldInsertMeasureWithLargeData", "project_measures", "measure_data"); + + ArgumentCaptor validMeasureModel = ArgumentCaptor.forClass(MeasureModel.class); + verify(memoryOptimizer).evictDataMeasure(eq(withLargeData), validMeasureModel.capture()); + assertThat(validMeasureModel.getValue().getMeasureData().getId()).isNotNull(); + assertThat(withLargeData.getId()).isNotNull(); } @Test public void should_not_save_best_values() { setupData("empty"); - 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(); + measurePersister.saveMeasure(aFile, new Measure(coverage()).setValue(100.0)); assertEmptyTables("project_measures", "measure_data"); } @@ -152,10 +156,7 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { public void should_not_save_memory_only_measures() { setupData("empty"); - 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(); + measurePersister.saveMeasure(aFile, new Measure("ncloc").setPersistenceMode(PersistenceMode.MEMORY)); assertEmptyTables("project_measures", "measure_data"); } @@ -164,17 +165,90 @@ public class MeasurePersisterTest extends AbstractDaoTestCase { public void should_always_save_non_file_measures() { setupData("empty"); - 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))); - - measurePersister.persist(); + 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)); + + checkTables("shouldInsertMeasure", "project_measures"); + } + + @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"); + } + @Test public void should_not_save_some_file_measures_with_best_value() { assertThat(MeasurePersister.shouldPersistMeasure(aFile, new Measure(CoreMetrics.LINES, 200.0))).isTrue(); 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 new file mode 100644 index 00000000000..468654da72c --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java @@ -0,0 +1,94 @@ +/* + * 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/qualitygate/QualityGateVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java index e79d27bc99d..9dd6a731f12 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java @@ -19,8 +19,6 @@ */ package org.sonar.batch.qualitygate; -import org.sonar.api.measures.Metric.Level; - import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.apache.commons.lang.NotImplementedException; @@ -34,13 +32,13 @@ import org.sonar.api.i18n.I18n; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Measure; import org.sonar.api.measures.Metric; +import org.sonar.api.measures.Metric.Level; import org.sonar.api.resources.File; import org.sonar.api.resources.Project; import org.sonar.api.resources.Resource; import org.sonar.api.test.IsMeasure; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; -import org.sonar.batch.index.DefaultIndex; import org.sonar.core.qualitygate.db.QualityGateConditionDto; import org.sonar.core.timemachine.Periods; @@ -48,8 +46,14 @@ import java.util.ArrayList; import java.util.Locale; import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Matchers.*; -import static org.mockito.Mockito.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class QualityGateVerifierTest { @@ -65,7 +69,6 @@ public class QualityGateVerifierTest { Periods periods; I18n i18n; Durations durations; - private DefaultIndex index; @Before public void before() { @@ -86,8 +89,7 @@ public class QualityGateVerifierTest { snapshot = mock(Snapshot.class); qualityGate = mock(QualityGate.class); when(qualityGate.isEnabled()).thenReturn(true); - index = mock(DefaultIndex.class); - verifier = new QualityGateVerifier(qualityGate, snapshot, periods, i18n, durations, index); + verifier = new QualityGateVerifier(qualityGate, snapshot, periods, i18n, durations); project = new Project("foo"); } @@ -128,8 +130,8 @@ public class QualityGateVerifierTest { verifier.decorate(project, context); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK))); verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString()))); verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_GATE_DETAILS, "{\"level\":\"OK\"," + "\"conditions\":" @@ -164,8 +166,8 @@ public class QualityGateVerifierTest { verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.WARN))); + verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN))); } @@ -182,8 +184,8 @@ public class QualityGateVerifierTest { verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, null))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.WARN))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.ERROR))); + verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN))); + verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.ERROR))); } @Test @@ -253,9 +255,9 @@ public class QualityGateVerifierTest { verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.OK))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureComplexity, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.OK))); } @Test @@ -281,9 +283,9 @@ public class QualityGateVerifierTest { verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.WARN))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.WARN))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureComplexity, Metric.Level.WARN))); + verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN))); + verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN))); + verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.WARN))); } @Test @@ -297,7 +299,7 @@ public class QualityGateVerifierTest { verifier.decorate(project, context); verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK))); } @Test @@ -315,7 +317,7 @@ public class QualityGateVerifierTest { verifier.decorate(project, context); verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureRatingMetric, Metric.Level.OK))); + verify(context).saveMeasure(argThat(hasLevel(measureRatingMetric, Metric.Level.OK))); } @Test @@ -330,7 +332,7 @@ public class QualityGateVerifierTest { verifier.decorate(project, context); verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null))); - verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.WARN))); + verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN))); } @Test(expected = NotImplementedException.class) @@ -403,14 +405,14 @@ public class QualityGateVerifierTest { verifier.decorate(project, context); // First call to saveMeasure is for the update of debt - verify(index).updateMeasure(eq(project), argThat(matchesMetric(metric, Level.ERROR, "The Debt > 1h"))); + verify(context).saveMeasure(argThat(matchesMetric(metric, Level.ERROR, "The Debt > 1h"))); verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "The Debt > 1h"))); verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_GATE_DETAILS, "{\"level\":\"ERROR\"," - + "\"conditions\":" - + "[" - + "{\"metric\":\"tech_debt\",\"op\":\"GT\",\"error\":\"3600\",\"actual\":\"7200.0\",\"level\":\"ERROR\"}" - + "]" - + "}"))); + + "\"conditions\":" + + "[" + + "{\"metric\":\"tech_debt\",\"op\":\"GT\",\"error\":\"3600\",\"actual\":\"7200.0\",\"level\":\"ERROR\"}" + + "]" + + "}"))); } private ArgumentMatcher matchesMetric(final Metric metric, final Metric.Level alertStatus, final String alertText) { 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 deleted file mode 100644 index a4da3d1d7c2..00000000000 --- a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java +++ /dev/null @@ -1,155 +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.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.Directory; -import org.sonar.api.resources.File; -import org.sonar.api.resources.Project; -import org.sonar.api.resources.Resource; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.rules.RulePriority; -import org.sonar.api.technicaldebt.batch.internal.DefaultCharacteristic; -import org.sonar.batch.index.Cache.Entry; -import org.sonar.batch.index.Caches; -import org.sonar.batch.index.CachesTest; - -import java.util.Iterator; - -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); - - Measure m = new Measure(CoreMetrics.NCLOC, 1.0); - cache.put(p, m); - - assertThat(cache.contains(p, m)).isTrue(); - assertThat(cache.entries()).hasSize(1); - Iterator> iterator = cache.entries().iterator(); - iterator.hasNext(); - Entry next = iterator.next(); - assertThat(next.value()).isEqualTo(m); - assertThat(next.key()[0]).isEqualTo("struts"); - - 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); - } - - @Test - public void should_add_measure_with_same_metric() throws Exception { - MeasureCache cache = new MeasureCache(caches); - Project p = new Project("struts"); - - assertThat(cache.entries()).hasSize(0); - assertThat(cache.byResource(p)).hasSize(0); - - Measure m1 = new Measure(CoreMetrics.NCLOC, 1.0); - Measure m2 = new Measure(CoreMetrics.NCLOC, 1.0).setCharacteristic(new DefaultCharacteristic().setKey("charac")); - Measure m3 = new Measure(CoreMetrics.NCLOC, 1.0).setPersonId(2); - Measure m4 = new RuleMeasure(CoreMetrics.NCLOC, RuleKey.of("repo", "rule"), RulePriority.BLOCKER, null); - cache.put(p, m1); - cache.put(p, m2); - cache.put(p, m3); - cache.put(p, m4); - - assertThat(cache.entries()).hasSize(4); - - assertThat(cache.byResource(p)).hasSize(4); - } - - @Test - public void should_get_measures() throws Exception { - MeasureCache cache = new MeasureCache(caches); - Project p = new Project("struts"); - Resource dir = new Directory("foo/bar").setEffectiveKey("struts:foo/bar"); - Resource file1 = new File("foo/bar/File1.txt").setEffectiveKey("struts:foo/bar/File1.txt"); - Resource file2 = new File("foo/bar/File2.txt").setEffectiveKey("struts:foo/bar/File2.txt"); - - assertThat(cache.entries()).hasSize(0); - - assertThat(cache.byResource(p)).hasSize(0); - assertThat(cache.byResource(dir)).hasSize(0); - - Measure mFile1 = new Measure(CoreMetrics.NCLOC, 1.0); - cache.put(file1, mFile1); - Measure mFile2 = new Measure(CoreMetrics.NCLOC, 3.0); - cache.put(file2, mFile2); - - assertThat(cache.entries()).hasSize(2); - assertThat(cache.byResource(p)).hasSize(0); - assertThat(cache.byResource(dir)).hasSize(0); - - Measure mDir = new Measure(CoreMetrics.NCLOC, 4.0); - cache.put(dir, mDir); - - assertThat(cache.entries()).hasSize(3); - assertThat(cache.byResource(p)).hasSize(0); - assertThat(cache.byResource(dir)).hasSize(1); - assertThat(cache.byResource(dir).iterator().next()).isEqualTo(mDir); - - Measure mProj = new Measure(CoreMetrics.NCLOC, 4.0); - cache.put(p, mProj); - - assertThat(cache.entries()).hasSize(4); - assertThat(cache.byResource(p)).hasSize(1); - assertThat(cache.byResource(p).iterator().next()).isEqualTo(mProj); - assertThat(cache.byResource(dir)).hasSize(1); - assertThat(cache.byResource(dir).iterator().next()).isEqualTo(mDir); - } -} 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 4eb61021ca5..866785feeef 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,8 +30,6 @@ 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; @@ -120,10 +118,8 @@ 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 5aa343120b6..e2448adb5da 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,7 +28,6 @@ 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; @@ -38,7 +37,7 @@ import java.util.Date; * * @since 1.10 */ -public class Measure implements Serializable { +public class Measure { 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; @@ -48,6 +47,8 @@ public class Measure implements Serializable { */ public static final int DEFAULT_PRECISION = 1; + // for internal use + private Long id; protected String metricKey; protected Metric metric; protected Double value; @@ -411,6 +412,24 @@ public class Measure implements Serializable { 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 @@ -627,7 +646,7 @@ public class Measure implements Serializable { return metric.isOptimizedBestValue() == Boolean.TRUE && metric.getBestValue() != null && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0) - && allNull(alertStatus, description, tendency, url, data) + && allNull(id, 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 c7731a1120a..e6c0c4912e1 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,12 +19,13 @@ */ package org.sonar.api.measures; +import java.util.Collection; /** * @since 1.10 */ public interface MeasuresFilter { - M filter(Iterable measures); + M filter(Collection 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 7a89e5f2030..4ed4a8ffb1b 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,7 +19,6 @@ */ 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; @@ -38,15 +37,8 @@ public final class MeasuresFilters { public static MeasuresFilter> all() { return new MeasuresFilter>() { - @Override - public Collection filter(Iterable measures) { - Collection all = new ArrayList(); - for (Measure measure : measures) { - if (measure != null) { - all.add(measure); - } - } - return all; + public Collection filter(Collection measures) { + return measures; } }; } @@ -57,8 +49,8 @@ public final class MeasuresFilters { public static MeasuresFilter metric(final String metricKey) { return new MetricFilter(metricKey) { - @Override - public Measure filter(Iterable measures) { + + public Measure filter(Collection measures) { if (measures == null) { return null; } @@ -78,8 +70,7 @@ public final class MeasuresFilters { public static MeasuresFilter characteristic(final Metric metric, final Characteristic characteristic) { return new MetricFilter(metric) { - @Override - public Measure filter(Iterable measures) { + public Measure filter(Collection measures) { if (measures == null) { return null; } @@ -109,8 +100,7 @@ public final class MeasuresFilters { public static MeasuresFilter requirement(final Metric metric, final Requirement requirement) { return new MetricFilter(metric) { - @Override - public Measure filter(Iterable measures) { + public Measure filter(Collection measures) { if (measures == null) { return null; } @@ -138,7 +128,7 @@ public final class MeasuresFilters { */ public static MeasuresFilter measure(final Measure measure) { return new MeasuresFilter() { - public Measure filter(Iterable measures) { + public Measure filter(Collection measures) { if (measures == null) { return null; } @@ -153,7 +143,7 @@ public final class MeasuresFilters { } public static MeasuresFilter rule(final Metric metric, final Rule rule) { - return new RuleFilter(metric, rule.ruleKey()); + return new RuleFilter(metric, rule); } public static MeasuresFilter> rules(final Metric metric) { @@ -161,11 +151,10 @@ public final class MeasuresFilters { private boolean apply(Measure measure) { return measure instanceof RuleMeasure && metric.equals(measure.getMetric()) - && measure.getPersonId() == null && ((RuleMeasure) measure).ruleKey() != null; + && measure.getPersonId() == null && ((RuleMeasure) measure).getRule() != null; } - @Override - public Collection filter(Iterable measures) { + public Collection filter(Collection measures) { if (measures == null) { return null; } @@ -213,8 +202,7 @@ public final class MeasuresFilters { abstract boolean doApply(RuleMeasure ruleMeasure); - @Override - public M filter(Iterable measures) { + public M filter(Collection measures) { if (measures == null) { return null; } @@ -228,17 +216,17 @@ public final class MeasuresFilters { } private static class RuleFilter extends AbstractRuleMeasureFilter { - private RuleKey ruleKey; + private Rule rule; - protected RuleFilter(Metric metric, RuleKey ruleKey) { + protected RuleFilter(Metric metric, Rule rule) { super(metric); - this.ruleKey = ruleKey; + this.rule = rule; } @Override boolean doApply(RuleMeasure measure) { - return measure.ruleKey() != null - && ruleKey.equals(measure.ruleKey()); + return measure.getRule() != null + && rule.equals(measure.getRule()); } } } 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 bc809c88884..dc7d507153b 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,16 +27,7 @@ import org.sonar.api.ServerExtension; import org.sonar.api.batch.InstantiationStrategy; import javax.annotation.Nullable; -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; +import javax.persistence.*; /** * This class represents the definition of a metric in Sonar. @@ -46,7 +37,7 @@ import java.io.Serializable; @Table(name = "metrics") @Entity(name = "Metric") @InstantiationStrategy(InstantiationStrategy.PER_BATCH) -public class Metric implements ServerExtension, BatchExtension, Serializable { +public class Metric implements ServerExtension, BatchExtension { /** * A metric bigger value means a degradation @@ -89,7 +80,7 @@ public class Metric implements ServerExtension, BatchExtension, Serializable { private Integer id; @Transient - private transient Formula formula; + private Formula formula; @Column(name = "name", updatable = false, nullable = false, length = 64) private String key; @@ -215,7 +206,7 @@ public class Metric implements ServerExtension, BatchExtension, Serializable { */ @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 b2feae04744..b0e1199a5c1 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,7 +22,6 @@ 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; @@ -33,54 +32,24 @@ import javax.annotation.Nullable; */ public class RuleMeasure extends Measure { - /** - * @deprecated since 4.4 - */ - @Deprecated - private transient Rule rule; - private RuleKey ruleKey; + private Rule rule; 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) { - 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.rule = rule; 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; } @@ -146,10 +115,10 @@ public class RuleMeasure extends Measure { } RuleMeasure other = (RuleMeasure) obj; return new EqualsBuilder() - .append(getMetric(), other.getMetric()) - .append(personId, other.personId) - .append(ruleKey, other.ruleKey) - .isEquals(); + .append(getMetric(), other.getMetric()) + .append(personId, other.personId) + .append(rule, other.rule) + .isEquals(); } @Override @@ -160,42 +129,35 @@ public class RuleMeasure extends Measure { @Override public int hashCode() { return new HashCodeBuilder(17, 37) - .append(getMetric()) - .append(personId) - .append(ruleKey) - .toHashCode(); + .append(getMetric()) + .append(personId) + .append(rule) + .toHashCode(); } @Override public String toString() { return new ToStringBuilder(this) - .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(); + .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(); } - /** - * @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, (RuleKey) null, priority, null).setValue(value); + return new RuleMeasure(metric, null, priority, null).setValue(value); } /** @@ -203,6 +165,6 @@ public class RuleMeasure extends Measure { */ @Deprecated public static RuleMeasure createForCategory(Metric metric, Integer category, @Nullable Double value) { - return new RuleMeasure(metric, (RuleKey) null, null, category).setValue(value); + return new RuleMeasure(metric, 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 402a5ec9f9d..39453f963ff 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,19 +34,7 @@ import org.sonar.check.Cardinality; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -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 javax.persistence.*; import java.util.ArrayList; import java.util.Date; @@ -291,15 +279,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; } @@ -480,6 +468,7 @@ public class Rule { return this; } + /** * For internal use only. * @@ -510,34 +499,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 b649a4e466a..fadad455575 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,7 +22,6 @@ package org.sonar.api.technicaldebt.batch; import javax.annotation.CheckForNull; -import java.io.Serializable; import java.util.Date; import java.util.List; @@ -31,7 +30,7 @@ import java.util.List; * @deprecated since 4.3 */ @Deprecated -public interface Characteristic extends Serializable { +public interface Characteristic { 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 71204e9ffb8..606d2a3fb18 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,7 +24,6 @@ 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; /** @@ -32,7 +31,7 @@ import java.util.Date; * @deprecated since 4.3 */ @Deprecated -public interface Requirement extends Serializable { +public interface Requirement { 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 ea075ed2a99..f9f569f199b 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,7 +25,6 @@ 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; @@ -38,8 +37,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)); } @@ -47,13 +46,10 @@ 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)); - Iterator filteredMeasures = MeasuresFilters.all().filter(measures).iterator(); - filteredMeasures.next(); - filteredMeasures.next(); - assertThat(filteredMeasures.hasNext(), is(false)); + assertThat(MeasuresFilters.all().filter(measures).size(), is(2)); } @Test @@ -62,13 +58,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)); } @@ -79,13 +75,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)); } @@ -94,10 +90,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 3f9f67ce236..091d8c781cd 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.ruleKey(), m.ruleKey()) && + ObjectUtils.equals(rule, m.getRule()) && NumberUtils.compare(value, m.getValue()) == 0; } } -- 2.39.5