]> source.dussan.org Git - sonarqube.git/commitdiff
Revert "SONAR-3437, SONAR-5189 Store measures in a persistit cache"
authorJulien HENRY <julien.henry@sonarsource.com>
Fri, 25 Apr 2014 09:17:09 +0000 (11:17 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Fri, 25 Apr 2014 09:21:03 +0000 (11:21 +0200)
This reverts commit 46395126b184c343df5471a147cdb48133f59f6f.

38 files changed:
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecorator.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/TendencyDecorator.java
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/VariationDecorator.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/issue/CountUnresolvedIssuesDecoratorTest.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/TendencyDecoratorTest.java
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/VariationDecoratorTest.java
sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java
sonar-batch/src/main/java/org/sonar/batch/debt/DebtDecorator.java
sonar-batch/src/main/java/org/sonar/batch/index/Bucket.java
sonar-batch/src/main/java/org/sonar/batch/index/Cache.java
sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
sonar-batch/src/main/java/org/sonar/batch/index/DefaultPersistenceManager.java
sonar-batch/src/main/java/org/sonar/batch/index/MeasurePersister.java
sonar-batch/src/main/java/org/sonar/batch/index/MemoryOptimizer.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/index/PersistenceManager.java
sonar-batch/src/main/java/org/sonar/batch/phases/PhaseExecutor.java
sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
sonar-batch/src/main/java/org/sonar/batch/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java [deleted file]
sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java [deleted file]
sonar-batch/src/test/java/org/sonar/batch/debt/DebtDecoratorTest.java
sonar-batch/src/test/java/org/sonar/batch/index/BucketTest.java
sonar-batch/src/test/java/org/sonar/batch/index/DefaultIndexTest.java
sonar-batch/src/test/java/org/sonar/batch/index/MeasurePersisterTest.java
sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java [new file with mode: 0644]
sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java [deleted file]
sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java
sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java
sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java
sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Characteristic.java
sonar-plugin-api/src/main/java/org/sonar/api/technicaldebt/batch/Requirement.java
sonar-plugin-api/src/test/java/org/sonar/api/measures/MeasuresFiltersTest.java
sonar-plugin-api/src/test/java/org/sonar/api/test/IsRuleMeasure.java

index 00c145353dd869a31f16fe62486d1624edc461d0..05ad24edf1c076ec75d756eb403449f644d1465c 100644 (file)
 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);
index 1e20c12f99431be39311d9418c6480a2f5b7707c..c824537cecea47a21d5c8128ca73ef77794774f4 100644 (file)
@@ -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);
         }
       }
     }
index be6d1a8f35edb31fd5040f8251559d5272442731..efebe9d7a62401a1d558ce5ee44cb653f5e2749d 100644 (file)
@@ -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);
       }
     }
   }
index 600f455ca21dd4f504a5aed457aa727105132aa0..41241336bd0089178aa4965d935514b869f1a884 100644 (file)
@@ -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());
     }
index a60dfbb5e9e2f76621ddc3cd3e69b0ddd5acdb26..8713c66537b226a8555c62cd984100d5cf60f61e 100644 (file)
@@ -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());
index 971d6d9f99fa43c5d4d0532602bbe6533ced8704..fe48596751c53e028bf31c61e8aabdd93d343883 100644 (file)
@@ -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);
   }
index 565417efad0b56885319a45708b963a04153899a..466be91dc79ec33b4c6ca85091eebb9296fb7cbb 100644 (file)
@@ -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));
index 1747593630821d82fadb6df466b5cec132093dfc..71e9cc36a1c27b3a4b82e20b0d6ae3671df16393 100644 (file)
@@ -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());
index ea4ed3b11a0219c58e420c8b98d39ccdfc8b7625..af6a87ae8a662401f4c642a8d9f998c9e3a20949 100644 (file)
  */
 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) {
index 39815dcfcb355f5191fa1d91e5769f9c85a2e31a..46f6bbf0a84f698c909340f174ad269421b272b9 100644 (file)
@@ -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.
    */
@@ -293,20 +293,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
    */
@@ -366,6 +352,7 @@ public class Cache<V extends Serializable> {
     }
   }
 
+
   //
   // LAZY ITERATORS AND ITERABLES
   //
index f95a5311c4e152415ec042c3bae80658f6480545..9624513e288a71e0d23d4eeb968e5cb9e458fd36 100644 (file)
@@ -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);
-  }
-
   //
   //
   //
index 39b9d8d472a32e7d7a6c622a906f9507832e6e42..14da97381cff207c3e47eb17f3dda2427bab5a82 100644 (file)
@@ -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);
index 80cac3ab4cb2ee7b398df4926a6b488a60ba5130..8e61a931bd0da86145e2aab288c86ba77a8c7fab 100644 (file)
 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 (file)
index 0000000..9daa941
--- /dev/null
@@ -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();
+    }
+  }
+
+}
index d90f63d8b666dfbba8d18d6c9b029a4e98d9aa69..d42e1a9f2e18d71086e9f821d85719e46ffc3814 100644 (file)
@@ -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);
index 90bafa7dc3e009577dcc21701a284ae39cd34a02..0ba16ea66d78cb223b9eb7f147b231d4645d3d3a 100644 (file)
@@ -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));
   }
 
index 406ca87648c90fdc12a3b39b096d32d5990fb9cb..bc77037311d2c87dc82342225a151fbf8c99f087 100644 (file)
@@ -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;
index 335884d7f8779e59058c5a888820ab5ca1f104d3..e4eacf6aa8d2aebc440b10e1e1432767beaddfca 100644 (file)
@@ -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 (file)
index b29b6c8..0000000
+++ /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 (file)
index 64f9c87..0000000
+++ /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;
index 6edbbab8fbf992941b99a199f173fab3a35929fa..fc82ece746c3539c71bfad28deff0fbe2ad5d316 100644 (file)
@@ -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());
     }
 
index cb20f15cde621d3f1841cbbd809d95b509a58d8a..2fc521a64d5e242a8582f5a02a58047b2036f454 100644 (file)
 package org.sonar.batch.index;
 
 import org.junit.Test;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilters;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.resources.Directory;
 import org.sonar.api.resources.File;
+import org.sonar.api.utils.SonarException;
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.core.IsNot.not;
@@ -48,6 +51,42 @@ public class BucketTest {
     assertThat(packageBucket.getChildren(), hasItem(fileBucket));
   }
 
+  @Test
+  public void shouldAddNewMeasure() {
+    Bucket fileBucket = new Bucket(javaFile);
+    Measure measure = new Measure(ncloc).setValue(1200.0);
+    fileBucket.addMeasure(measure);
+
+    assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1));
+    assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)), is(measure));
+  }
+
+  @Test
+  public void shouldUpdateMeasure() {
+    Bucket fileBucket = new Bucket(javaFile);
+    Measure measure = new Measure(ncloc).setValue(1200.0);
+    fileBucket.addMeasure(measure);
+
+    assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1));
+    assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)).getValue(), is(1200.0));
+
+    measure.setValue(500.0);
+    fileBucket.addMeasure(measure);
+
+    assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1));
+    assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)).getValue(), is(500.0));
+  }
+
+  @Test(expected = SonarException.class)
+  public void shouldFailIfAddingSameMeasures() {
+    Bucket fileBucket = new Bucket(javaFile);
+    Measure measure = new Measure(ncloc).setValue(1200.0);
+    fileBucket.addMeasure(measure);
+
+    measure = new Measure(ncloc).setValue(500.0);
+    fileBucket.addMeasure(measure);
+  }
+
   @Test
   public void shouldBeEquals() {
     assertEquals(new Bucket(directory), new Bucket(directory));
index 9a162fbf42549f58af29057a4431a2cb8b037a6b..4abc71694cf1ffc6c24fa9c1bbbe89fb1b96a349 100644 (file)
@@ -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");
index bde25e7ddc0fb9d85bd940f838fb683693217482..fc3a69628d28a6cb26a0bb2985096d9f05e462fa 100644 (file)
@@ -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,17 +165,90 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
   public void should_always_save_non_file_measures() {
     setupData("empty");
 
-    Measure measure1 = new Measure(ncloc()).setValue(200.0);
-    Measure measure2 = new Measure(ncloc()).setValue(300.0);
-    when(measureCache.entries()).thenReturn(Arrays.asList(
-      new Cache.Entry<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();
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 (file)
index 0000000..468654d
--- /dev/null
@@ -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;
+  }
+}
index e79d27bc99d4bc5654503f07c8d1a07a58a2a52e..9dd6a731f12ef9c82da982910873f45df47f5594 100644 (file)
@@ -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 (file)
index a4da3d1..0000000
+++ /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);
-  }
-}
index 4eb61021ca55b4d1d3de6f50bde66d6a05ae6caa..866785feeefc08867da5df78e147e2aea962bb28 100644 (file)
@@ -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);
 
   /**
index 5aa343120b6c61655a3190166f59078b01f5348f..e2448adb5da7f0b512150a9b9dfe30039887d05c 100644 (file)
@@ -28,7 +28,6 @@ import org.sonar.api.technicaldebt.batch.Requirement;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
-import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.Date;
@@ -38,7 +37,7 @@ import java.util.Date;
  *
  * @since 1.10
  */
-public class Measure implements Serializable {
+public class Measure {
   private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5";
 
   protected static final int MAX_TEXT_SIZE = 96;
@@ -48,6 +47,8 @@ public class Measure implements Serializable {
    */
   public static final int DEFAULT_PRECISION = 1;
 
+  // for internal use
+  private Long id;
   protected String metricKey;
   protected Metric metric;
   protected Double value;
@@ -411,6 +412,24 @@ public class Measure implements Serializable {
     return this;
   }
 
+  /**
+   * @return the measure id - Internal use only
+   */
+  public Long getId() {
+    return id;
+  }
+
+  /**
+   * Sets the measure id - Internal use only
+   *
+   * @param id the id
+   * @return the measure object instance
+   */
+  public Measure setId(Long id) {
+    this.id = id;
+    return this;
+  }
+
   /**
    * @return the first variation value
    * @since 2.5
@@ -627,7 +646,7 @@ public class Measure implements Serializable {
     return metric.isOptimizedBestValue() == Boolean.TRUE
       && metric.getBestValue() != null
       && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0)
-      && allNull(alertStatus, description, tendency, url, data)
+      && allNull(id, alertStatus, description, tendency, url, data)
       && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
   }
 
index c7731a1120a578422e629cadee5757209ad6693d..e6c0c4912e1f39a186e661349f5e91811949eb4d 100644 (file)
  */
 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);
 
 }
index 7a89e5f2030704cc52edf5ada1311e760ded89de..4ed4a8ffb1b1512f1662770f65a69687add63085 100644 (file)
@@ -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());
     }
   }
 }
index bc809c88884f32cd96b292de045654393078e4fb..dc7d507153b78b164850331937ec8eb240ce3d30 100644 (file)
@@ -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;
index b2feae04744af4a67a86cab8790da05e65db9242..b0e1199a5c128f160d039865da1b36e4a67a6365 100644 (file)
@@ -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);
   }
 }
index 402a5ec9f9d12e53e8cf5a49064174b15d10668e..39453f963ff39a9d51dda44f7e402a4bf6cfa507 100644 (file)
@@ -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
index b649a4e466acacbfdffa8bc94cc37531dadbffb7..fadad455575287ea637f92684e01394927319260 100644 (file)
@@ -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();
 
index 71204e9ffb837dac9eb4947825614c36614f1854..606d2a3fb1822ea65cec7c25e34c3c6d600fdea3 100644 (file)
@@ -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();
 
index ea075ed2a9900a37623fc787658a7a81ec42b63c..f9f569f199b55e4d1c36027c9b558dbc9e538f5c 100644 (file)
@@ -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));
   }
index 3f9f67ce23645ed2b8bd27a9128662cf0bc0f5d2..091d8c781cd9830fd55f6bb8596947d25b2d07de 100644 (file)
@@ -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;
   }
 }