aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java79
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyDecorator.java18
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java34
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java21
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyDecoratorTest.java7
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java27
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java8
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java3
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/Bucket.java42
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/Cache.java17
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java49
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java24
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java214
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java117
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java9
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java9
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java27
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java33
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java82
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java27
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java12
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/BucketTest.java39
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java12
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java158
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java94
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java60
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java155
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java4
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java25
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java44
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java17
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java86
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java55
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java3
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Requirement.java3
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasuresFiltersTest.java46
-rw-r--r--sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java2
38 files changed, 875 insertions, 790 deletions
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<RulePriority> severityBag = HashMultiset.create();
- Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity = Maps.newHashMap();
+ Map<RulePriority, Multiset<Rule>> rulesPerSeverity = Maps.newHashMap();
ListMultimap<RulePriority, Issue> 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<RuleKey> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
- rulesBag.add(issue.ruleKey());
+ Multiset<Rule> 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<RulePriority, Multiset<RuleKey>> rulesPerSeverity) {
+ private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<Rule>> rulesPerSeverity) {
Metric metric = SeverityUtils.severityToIssueMetric(severity);
Collection<Measure> 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<RuleKey> rulesBag = initRules(rulesPerSeverity, severity);
- rulesBag.add(ruleKey, childRuleMeasure.getIntValue());
+ Rule rule = childRuleMeasure.getRule();
+ if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) {
+ Multiset<Rule> rulesBag = initRules(rulesPerSeverity, severity);
+ rulesBag.add(rule, childRuleMeasure.getIntValue());
}
}
- Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity);
+ Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
if (rulesBag != null) {
- for (Multiset.Entry<RuleKey> entry : rulesBag.entrySet()) {
+ for (Multiset.Entry<Rule> 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<Issue> issues, boolean shouldSaveNewMetrics) {
if (shouldSaveNewMetrics) {
Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
- ListMultimap<RuleKey, Measure> childMeasuresPerRuleKeys = ArrayListMultimap.create();
- ListMultimap<RuleKey, Issue> issuesPerRuleKeys = ArrayListMultimap.create();
- Set<RuleKey> ruleKeys = Sets.newHashSet();
+ ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create();
+ ListMultimap<Rule, Issue> issuesPerRule = ArrayListMultimap.create();
+ Set<Rule> rules = Sets.newHashSet();
Collection<Measure> 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<RuleKey> initRules(Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity, RulePriority severity) {
- Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity);
+ private Multiset<Rule> initRules(Map<RulePriority, Multiset<Rule>> rulesPerSeverity, RulePriority severity) {
+ Multiset<Rule> 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<Metric> 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<PastSnapshot> 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<PastSnapshot> projectPastSnapshots, RuleFinder ruleFinder, DefaultIndex index) {
+ VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List<PastSnapshot> 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<Object[]> 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<Object[]> pastMeasures) {
+ void compareWithPastMeasures(DecoratorContext context, int index, List<Object[]> pastMeasures) {
Map<MeasureKey, Object[]> 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.<MeasuresFilter>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.<Measure>anyObject());
+ verify(context, times(3)).saveMeasure(Matchers.<Measure>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.<MeasuresFilter>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.<Measure>anyObject());
+ verify(context, times(3)).saveMeasure(Matchers.<Measure>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<String, Measure> measuresByMetric = ArrayListMultimap.create();
private Bucket parent;
private List<Bucket> children;
@@ -70,7 +79,30 @@ public final class Bucket {
return parent;
}
+ public void addMeasure(Measure measure) {
+ List<Measure> 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> M getMeasures(final MeasuresFilter<M> filter) {
+ Collection<Measure> 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<V extends Serializable> {
}
}
+
/**
* Clears the default as well as all group caches.
*/
@@ -294,20 +294,6 @@ public class Cache<V extends Serializable> {
}
/**
- * Lazy-loading values for given keys
- */
- public Iterable<V> values(Object firstKey, Object secondKey) {
- try {
- exchange.clear();
- exchange.append(firstKey).append(secondKey).append(Key.BEFORE);
- Exchange iteratorExchange = new Exchange(exchange);
- return new ValueIterable<V>(iteratorExchange, false);
- } catch (Exception e) {
- throw new IllegalStateException("Fail to get values from cache " + name, e);
- }
- }
-
- /**
* Lazy-loading values for a given key
*/
public Iterable<V> values(Object key) {
@@ -366,6 +352,7 @@ public class Cache<V extends Serializable> {
}
}
+
//
// 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> M getMeasures(Resource resource, MeasuresFilter<M> filter) {
- // Reload resource so that effective key is populated
- Resource indexedResource = getResource(resource);
- Iterable<Measure> 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<Resource, Measure> 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<Measure> 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<MeasureModelAndDetails> getMeasuresToSave() {
+ List<MeasureModelAndDetails> measures = Lists.newArrayList();
+
+ Map<Resource, Collection<Measure>> map = unsavedMeasuresByResource.asMap();
+ for (Map.Entry<Resource, Collection<Measure>> 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<MeasureModelAndDetails> 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<Measure> loadedMeasures = Lists.newArrayList();
+ private Map<Long, Integer> 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<Metric> dependsUponMetrics() {
Set<Metric> 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<String> 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<Measure> cache;
-
- public MeasureCache(Caches caches) {
- cache = caches.createCache("measures");
- }
-
- public Iterable<Entry<Measure>> entries() {
- return cache.entries();
- }
-
- public Iterable<Measure> 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;
@@ -49,6 +52,42 @@ public class BucketTest {
}
@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));
assertEquals(new Bucket(directory).hashCode(), new Bucket(directory).hashCode());
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<Measure>(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<Measure>(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<Measure>(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<Measure>(new String[] {"foo", "ncloc"}, withLargeData)));
-
- measurePersister.persist();
+ measurePersister.saveMeasure(project, withLargeData);
checkTables("shouldInsertMeasureWithLargeData", "project_measures", "measure_data");
+
+ ArgumentCaptor<MeasureModel> 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<Measure>(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<Measure>(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,18 +165,91 @@ 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<Measure>(new String[] {"foo", "ncloc"}, measure1),
- new Cache.Entry<Measure>(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();
assertThat(MeasurePersister.shouldPersistMeasure(aFile, new Measure(CoreMetrics.DUPLICATED_LINES_DENSITY, 3.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<Measure> 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<Entry<Measure>> iterator = cache.entries().iterator();
- iterator.hasNext();
- Entry<Measure> 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<Resource, Depe
@Deprecated
public abstract Resource addResource(Resource resource);
- @CheckForNull
public abstract Measure getMeasure(Resource resource, Metric metric);
- @CheckForNull
public abstract <M> M getMeasures(Resource resource, MeasuresFilter<M> 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;
@@ -412,6 +413,24 @@ public class Measure implements Serializable {
}
/**
+ * @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> {
- M filter(Iterable<Measure> measures);
+ M filter(Collection<Measure> 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<Collection<Measure>> all() {
return new MeasuresFilter<Collection<Measure>>() {
- @Override
- public Collection<Measure> filter(Iterable<Measure> measures) {
- Collection<Measure> all = new ArrayList<Measure>();
- for (Measure measure : measures) {
- if (measure != null) {
- all.add(measure);
- }
- }
- return all;
+ public Collection<Measure> filter(Collection<Measure> measures) {
+ return measures;
}
};
}
@@ -57,8 +49,8 @@ public final class MeasuresFilters {
public static MeasuresFilter<Measure> metric(final String metricKey) {
return new MetricFilter<Measure>(metricKey) {
- @Override
- public Measure filter(Iterable<Measure> measures) {
+
+ public Measure filter(Collection<Measure> measures) {
if (measures == null) {
return null;
}
@@ -78,8 +70,7 @@ public final class MeasuresFilters {
public static MeasuresFilter<Measure> characteristic(final Metric metric, final Characteristic characteristic) {
return new MetricFilter<Measure>(metric) {
- @Override
- public Measure filter(Iterable<Measure> measures) {
+ public Measure filter(Collection<Measure> measures) {
if (measures == null) {
return null;
}
@@ -109,8 +100,7 @@ public final class MeasuresFilters {
public static MeasuresFilter<Measure> requirement(final Metric metric, final Requirement requirement) {
return new MetricFilter<Measure>(metric) {
- @Override
- public Measure filter(Iterable<Measure> measures) {
+ public Measure filter(Collection<Measure> measures) {
if (measures == null) {
return null;
}
@@ -138,7 +128,7 @@ public final class MeasuresFilters {
*/
public static MeasuresFilter<Measure> measure(final Measure measure) {
return new MeasuresFilter<Measure>() {
- public Measure filter(Iterable<Measure> measures) {
+ public Measure filter(Collection<Measure> measures) {
if (measures == null) {
return null;
}
@@ -153,7 +143,7 @@ public final class MeasuresFilters {
}
public static MeasuresFilter<RuleMeasure> rule(final Metric metric, final Rule rule) {
- return new RuleFilter(metric, rule.ruleKey());
+ return new RuleFilter(metric, rule);
}
public static MeasuresFilter<Collection<RuleMeasure>> 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<RuleMeasure> filter(Iterable<Measure> measures) {
+ public Collection<RuleMeasure> filter(Collection<Measure> measures) {
if (measures == null) {
return null;
}
@@ -213,8 +202,7 @@ public final class MeasuresFilters {
abstract boolean doApply(RuleMeasure ruleMeasure);
- @Override
- public M filter(Iterable<Measure> measures) {
+ public M filter(Collection<Measure> measures) {
if (measures == null) {
return null;
}
@@ -228,17 +216,17 @@ public final class MeasuresFilters {
}
private static class RuleFilter extends AbstractRuleMeasureFilter<RuleMeasure> {
- 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<Measure> filter = MeasuresFilters.metric(CoreMetrics.VIOLATIONS);
Collection<Measure> 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<Measure> 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<Measure> 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<RuleMeasure> filter = MeasuresFilters.rule(CoreMetrics.VIOLATIONS, rule1);
List<Measure> 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<Collection<RuleMeasure>> filter = MeasuresFilters.rules(CoreMetrics.VIOLATIONS);
List<Measure> 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<Measure> filter = MeasuresFilters.measure(new Measure(CoreMetrics.VIOLATIONS));
List<Measure> 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<Measure> {
}
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;
}
}