]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-3437, SONAR-5189 Store measures in a persistit cache
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 23 Apr 2014 12:41:32 +0000 (14:41 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Thu, 24 Apr 2014 08:20:15 +0000 (10:20 +0200)
34 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/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/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 [deleted file]
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/scan/ProjectScanContainer.java
sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java [new file with mode: 0644]
sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java [new file with mode: 0644]
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 [deleted file]
sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java [new file with mode: 0644]
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 05ad24edf1c076ec75d756eb403449f644d1465c..00c145353dd869a31f16fe62486d1624edc461d0 100644 (file)
 package org.sonar.plugins.core.issue;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.*;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multiset;
+import com.google.common.collect.Sets;
 import org.apache.commons.lang.time.DateUtils;
-import org.sonar.api.batch.*;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorBarriers;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
 import org.sonar.api.component.ResourcePerspectives;
 import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.measures.*;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.MeasuresFilters;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.RuleMeasure;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.rules.RulePriority;
@@ -38,7 +54,12 @@ import org.sonar.batch.components.TimeMachineConfiguration;
 
 import javax.annotation.Nullable;
 
-import java.util.*;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Computes metrics related to number of issues.
@@ -80,7 +101,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator {
       CoreMetrics.OPEN_ISSUES,
       CoreMetrics.REOPENED_ISSUES,
       CoreMetrics.CONFIRMED_ISSUES
-    );
+      );
   }
 
   public void decorate(Resource resource, DecoratorContext context) {
@@ -90,7 +111,7 @@ public class CountUnresolvedIssuesDecorator implements Decorator {
       boolean shouldSaveNewMetrics = shouldSaveNewMetrics(context);
 
       Multiset<RulePriority> severityBag = HashMultiset.create();
-      Map<RulePriority, Multiset<Rule>> rulesPerSeverity = Maps.newHashMap();
+      Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity = Maps.newHashMap();
       ListMultimap<RulePriority, Issue> issuesPerSeverity = ArrayListMultimap.create();
       int countOpen = 0;
       int countReopened = 0;
@@ -98,8 +119,8 @@ public class CountUnresolvedIssuesDecorator implements Decorator {
 
       for (Issue issue : issues) {
         severityBag.add(RulePriority.valueOf(issue.severity()));
-        Multiset<Rule> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
-        rulesBag.add(rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule()));
+        Multiset<RuleKey> rulesBag = initRules(rulesPerSeverity, RulePriority.valueOf(issue.severity()));
+        rulesBag.add(issue.ruleKey());
         issuesPerSeverity.put(RulePriority.valueOf(issue.severity()), issue);
 
         if (Issue.STATUS_OPEN.equals(issue.status())) {
@@ -159,22 +180,22 @@ public class CountUnresolvedIssuesDecorator implements Decorator {
     }
   }
 
-  private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<Rule>> rulesPerSeverity) {
+  private void saveIssuesPerRules(DecoratorContext context, RulePriority severity, Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity) {
     Metric metric = SeverityUtils.severityToIssueMetric(severity);
 
     Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
     for (Measure child : children) {
       RuleMeasure childRuleMeasure = (RuleMeasure) child;
-      Rule rule = childRuleMeasure.getRule();
-      if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) {
-        Multiset<Rule> rulesBag = initRules(rulesPerSeverity, severity);
-        rulesBag.add(rule, childRuleMeasure.getIntValue());
+      RuleKey ruleKey = childRuleMeasure.ruleKey();
+      if (ruleKey != null && MeasureUtils.hasValue(childRuleMeasure)) {
+        Multiset<RuleKey> rulesBag = initRules(rulesPerSeverity, severity);
+        rulesBag.add(ruleKey, childRuleMeasure.getIntValue());
       }
     }
 
-    Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
+    Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity);
     if (rulesBag != null) {
-      for (Multiset.Entry<Rule> entry : rulesBag.entrySet()) {
+      for (Multiset.Entry<RuleKey> entry : rulesBag.entrySet()) {
         RuleMeasure measure = RuleMeasure.createForRule(metric, entry.getElement(), (double) entry.getCount());
         measure.setSeverity(severity);
         context.saveMeasure(measure);
@@ -185,34 +206,34 @@ public class CountUnresolvedIssuesDecorator implements Decorator {
   private void saveNewIssuesPerRule(DecoratorContext context, RulePriority severity, Collection<Issue> issues, boolean shouldSaveNewMetrics) {
     if (shouldSaveNewMetrics) {
       Metric metric = SeverityUtils.severityToNewMetricIssue(severity);
-      ListMultimap<Rule, Measure> childMeasuresPerRule = ArrayListMultimap.create();
-      ListMultimap<Rule, Issue> issuesPerRule = ArrayListMultimap.create();
-      Set<Rule> rules = Sets.newHashSet();
+      ListMultimap<RuleKey, Measure> childMeasuresPerRuleKeys = ArrayListMultimap.create();
+      ListMultimap<RuleKey, Issue> issuesPerRuleKeys = ArrayListMultimap.create();
+      Set<RuleKey> ruleKeys = Sets.newHashSet();
 
       Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(metric));
       for (Measure child : children) {
         RuleMeasure childRuleMeasure = (RuleMeasure) child;
-        Rule rule = childRuleMeasure.getRule();
-        if (rule != null) {
-          childMeasuresPerRule.put(rule, childRuleMeasure);
-          rules.add(rule);
+        RuleKey ruleKey = childRuleMeasure.ruleKey();
+        if (ruleKey != null) {
+          childMeasuresPerRuleKeys.put(ruleKey, childRuleMeasure);
+          ruleKeys.add(ruleKey);
         }
       }
 
       for (Issue issue : issues) {
         if (RulePriority.valueOf(issue.severity()).equals(severity)) {
-          Rule rule = rulefinder.findByKey(issue.ruleKey().repository(), issue.ruleKey().rule());
-          rules.add(rule);
-          issuesPerRule.put(rule, issue);
+          ruleKeys.add(issue.ruleKey());
+          issuesPerRuleKeys.put(issue.ruleKey(), issue);
         }
       }
 
-      for (Rule rule : rules) {
+      for (RuleKey ruleKey : ruleKeys) {
+        Rule rule = rulefinder.findByKey(ruleKey);
         RuleMeasure measure = RuleMeasure.createForRule(metric, rule, null);
         measure.setSeverity(severity);
         for (Period period : timeMachineConfiguration.periods()) {
           int variationIndex = period.getIndex();
-          double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRule.get(rule)) + countIssues(issuesPerRule.get(rule), period);
+          double sum = MeasureUtils.sumOnVariation(true, variationIndex, childMeasuresPerRuleKeys.get(rule.ruleKey())) + countIssues(issuesPerRuleKeys.get(rule.ruleKey()), period);
           measure.setVariation(variationIndex, sum);
         }
         context.saveMeasure(measure);
@@ -242,8 +263,8 @@ public class CountUnresolvedIssuesDecorator implements Decorator {
     return sum;
   }
 
-  private Multiset<Rule> initRules(Map<RulePriority, Multiset<Rule>> rulesPerSeverity, RulePriority severity) {
-    Multiset<Rule> rulesBag = rulesPerSeverity.get(severity);
+  private Multiset<RuleKey> initRules(Map<RulePriority, Multiset<RuleKey>> rulesPerSeverity, RulePriority severity) {
+    Multiset<RuleKey> rulesBag = rulesPerSeverity.get(severity);
     if (rulesBag == null) {
       rulesBag = HashMultiset.create();
       rulesPerSeverity.put(severity, rulesBag);
index efebe9d7a62401a1d558ce5ee44cb653f5e2749d..30f5edcd082014c50dbe4d35f7293f7cac56f150 100644 (file)
@@ -21,12 +21,21 @@ package org.sonar.plugins.core.timemachine;
 
 import com.google.common.collect.Maps;
 import org.apache.commons.lang.StringUtils;
-import org.sonar.api.batch.*;
-import org.sonar.api.measures.*;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorBarriers;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilters;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.MetricFinder;
+import org.sonar.api.measures.RuleMeasure;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.Scopes;
+import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.technicaldebt.batch.Characteristic;
 import org.sonar.batch.components.PastMeasuresLoader;
 import org.sonar.batch.components.PastSnapshot;
@@ -42,16 +51,17 @@ public class VariationDecorator implements Decorator {
   private List<PastSnapshot> projectPastSnapshots;
   private MetricFinder metricFinder;
   private PastMeasuresLoader pastMeasuresLoader;
+  private RuleFinder ruleFinder;
 
-
-  public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration timeMachineConfiguration) {
-    this(pastMeasuresLoader, metricFinder, timeMachineConfiguration.getProjectPastSnapshots());
+  public VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, TimeMachineConfiguration timeMachineConfiguration, RuleFinder ruleFinder) {
+    this(pastMeasuresLoader, metricFinder, timeMachineConfiguration.getProjectPastSnapshots(), ruleFinder);
   }
 
-  VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List<PastSnapshot> projectPastSnapshots) {
+  VariationDecorator(PastMeasuresLoader pastMeasuresLoader, MetricFinder metricFinder, List<PastSnapshot> projectPastSnapshots, RuleFinder ruleFinder) {
     this.pastMeasuresLoader = pastMeasuresLoader;
     this.projectPastSnapshots = projectPastSnapshots;
     this.metricFinder = metricFinder;
+    this.ruleFinder = ruleFinder;
   }
 
   public boolean shouldExecuteOnProject(Project project) {
@@ -98,7 +108,7 @@ public class VariationDecorator implements Decorator {
       Characteristic characteristic = measure.getCharacteristic();
       Integer characteristicId = characteristic != null ? characteristic.id() : null;
       Integer personId = measure.getPersonId();
-      Integer ruleId = measure instanceof RuleMeasure ? ((RuleMeasure) measure).getRule().getId() : null;
+      Integer ruleId = measure instanceof RuleMeasure ? ruleFinder.findByKey(((RuleMeasure) measure).ruleKey()).getId() : null;
 
       Object[] pastMeasure = pastMeasuresByKey.get(new MeasureKey(metricId, characteristicId, personId, ruleId));
       if (updateVariation(measure, pastMeasure, index)) {
index 41241336bd0089178aa4965d935514b869f1a884..4ee5cb5934ba4603110c6aefa4023c2a69e6af57 100644 (file)
@@ -31,7 +31,11 @@ import org.sonar.api.component.ResourcePerspectives;
 import org.sonar.api.issue.Issuable;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.internal.DefaultIssue;
-import org.sonar.api.measures.*;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.RuleMeasure;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.resources.Scopes;
@@ -55,7 +59,12 @@ import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyDouble;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
 
 public class CountUnresolvedIssuesDecoratorTest {
 
@@ -324,7 +333,7 @@ public class CountUnresolvedIssuesDecoratorTest {
       }
       RuleMeasure m = (RuleMeasure) o;
       return ObjectUtils.equals(metric, m.getMetric()) &&
-        ObjectUtils.equals(rule, m.getRule()) &&
+        ObjectUtils.equals(rule.ruleKey(), m.ruleKey()) &&
         ObjectUtils.equals(var1, m.getVariation1()) &&
         ObjectUtils.equals(var2, m.getVariation2());
     }
index fe48596751c53e028bf31c61e8aabdd93d343883..c7c1876bf6cdb0e21f2f5ba7761055dfd965185e 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.api.resources.File;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.Resource;
 import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleFinder;
 import org.sonar.batch.components.PastMeasuresLoader;
 import org.sonar.batch.components.PastSnapshot;
 import org.sonar.batch.components.TimeMachineConfiguration;
@@ -60,7 +61,7 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase {
   @Test
   public void shouldComputeVariations() {
     TimeMachineConfiguration timeMachineConfiguration = mock(TimeMachineConfiguration.class);
-    VariationDecorator decorator = new VariationDecorator(mock(PastMeasuresLoader.class), mock(MetricFinder.class), timeMachineConfiguration);
+    VariationDecorator decorator = new VariationDecorator(mock(PastMeasuresLoader.class), mock(MetricFinder.class), timeMachineConfiguration, mock(RuleFinder.class));
 
     assertThat(decorator.shouldComputeVariation(new Project("foo"))).isTrue();
     assertThat(decorator.shouldComputeVariation(new File("foo/bar.c"))).isFalse();
@@ -89,7 +90,7 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase {
     Measure currentCoverage = newMeasure(COVERAGE, 80.0);
     when(context.getMeasures(Matchers.<MeasuresFilter>anyObject())).thenReturn(Arrays.asList(currentNcloc, currentCoverage));
 
-    VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3));
+    VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1, pastSnapshot3), mock(RuleFinder.class));
     decorator.decorate(dir, context);
 
     // context updated for each variation : 2 times for ncloc and 1 time for coverage
@@ -106,11 +107,16 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase {
 
   @Test
   public void shouldComputeVariationOfRuleMeasures() {
-    Rule rule1 = Rule.create();
+    RuleFinder ruleFinder = mock(RuleFinder.class);
+
+    Rule rule1 = Rule.create("repo", "rule1");
     rule1.setId(1);
-    Rule rule2 = Rule.create();
+    Rule rule2 = Rule.create("repo", "rule2");
     rule2.setId(2);
 
+    when(ruleFinder.findByKey(rule1.ruleKey())).thenReturn(rule1);
+    when(ruleFinder.findByKey(rule2.ruleKey())).thenReturn(rule2);
+
     Resource dir = new Directory("org/foo");
 
     PastMeasuresLoader pastMeasuresLoader = mock(PastMeasuresLoader.class);
@@ -129,7 +135,7 @@ public class VariationDecoratorTest extends AbstractDbUnitTestCase {
     Measure violationsRule2 = RuleMeasure.createForRule(VIOLATIONS, rule2, 70.0);
     when(context.getMeasures(Matchers.<MeasuresFilter>anyObject())).thenReturn(Arrays.asList(violations, violationsRule1, violationsRule2));
 
-    VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1));
+    VariationDecorator decorator = new VariationDecorator(pastMeasuresLoader, mock(MetricFinder.class), Arrays.asList(pastSnapshot1), ruleFinder);
     decorator.decorate(dir, context);
 
     // context updated for each variation
index 466be91dc79ec33b4c6ca85091eebb9296fb7cbb..565417efad0b56885319a45708b963a04153899a 100644 (file)
@@ -38,7 +38,12 @@ import org.sonar.batch.index.DefaultIndex;
 import javax.annotation.Nullable;
 import javax.persistence.Query;
 
-import java.util.*;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 public class DefaultTimeMachine implements TimeMachine {
 
@@ -155,7 +160,6 @@ public class DefaultTimeMachine implements TimeMachine {
   static Measure toMeasure(MeasureModel model, Metric metric, @Nullable Characteristic characteristic) {
     // NOTE: measures on rule are not supported
     Measure measure = new Measure(metric);
-    measure.setId(model.getId());
     measure.setDescription(model.getDescription());
     measure.setValue(model.getValue());
     measure.setData(model.getData(metric));
index 71e9cc36a1c27b3a4b82e20b0d6ae3671df16393..1747593630821d82fadb6df466b5cec132093dfc 100644 (file)
@@ -48,6 +48,7 @@ import org.sonar.api.technicaldebt.batch.TechnicalDebtModel;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -110,7 +111,7 @@ public final class DebtDecorator implements Decorator {
     for (Measure measure : context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.TECHNICAL_DEBT))) {
       Long debt = measure.getValue().longValue();
       RuleMeasure ruleMeasure = (RuleMeasure) measure;
-      total += computeDebt(debt, ruleMeasure.getRule().ruleKey(), ruleDebts, characteristicDebts);
+      total += computeDebt(debt, ruleMeasure.ruleKey(), ruleDebts, characteristicDebts);
     }
 
     context.saveMeasure(CoreMetrics.TECHNICAL_DEBT, total.doubleValue());
index af6a87ae8a662401f4c642a8d9f998c9e3a20949..ea4ed3b11a0219c58e420c8b98d39ccdfc8b7625 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;
@@ -79,30 +70,7 @@ 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);
@@ -110,16 +78,6 @@ 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 46f6bbf0a84f698c909340f174ad269421b272b9..13e238fce5b7240d6cb2fbadd84a2333800fc73a 100644 (file)
@@ -26,6 +26,7 @@ import com.persistit.exception.PersistitException;
 import org.apache.commons.lang.builder.ToStringBuilder;
 
 import javax.annotation.CheckForNull;
+
 import java.io.Serializable;
 import java.util.Iterator;
 import java.util.Set;
@@ -219,7 +220,6 @@ public class Cache<V extends Serializable> {
     }
   }
 
-
   /**
    * Clears the default as well as all group caches.
    */
@@ -293,6 +293,20 @@ 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
    */
@@ -307,6 +321,20 @@ public class Cache<V extends Serializable> {
     }
   }
 
+  /**
+   * Lazy-loading values for a given key
+   */
+  public Iterable<V> allValues(Object key) {
+    try {
+      exchange.clear();
+      exchange.append(key).append(Key.BEFORE);
+      Exchange iteratorExchange = new Exchange(exchange);
+      return new ValueIterable<V>(iteratorExchange, true);
+    } catch (Exception e) {
+      throw new IllegalStateException("Fail to get values from cache " + name, e);
+    }
+  }
+
   /**
    * Lazy-loading values
    */
@@ -352,7 +380,6 @@ public class Cache<V extends Serializable> {
     }
   }
 
-
   //
   // LAZY ITERATORS AND ITERABLES
   //
index 9624513e288a71e0d23d4eeb968e5cb9e458fd36..cea7aa71729e9cd769220b968981dba72a0cb6aa 100644 (file)
@@ -52,6 +52,7 @@ import org.sonar.api.violations.ViolationQuery;
 import org.sonar.batch.ProjectTree;
 import org.sonar.batch.issue.DeprecatedViolations;
 import org.sonar.batch.issue.ModuleIssues;
+import org.sonar.batch.scan.measure.MeasureCache;
 import org.sonar.core.component.ComponentKeys;
 import org.sonar.core.component.ScanGraph;
 
@@ -85,17 +86,19 @@ public class DefaultIndex extends SonarIndex {
   private ProjectTree projectTree;
   private final DeprecatedViolations deprecatedViolations;
   private ModuleIssues moduleIssues;
+  private final MeasureCache measureCache;
 
   private ResourceKeyMigration migration;
 
   public DefaultIndex(PersistenceManager persistence, ProjectTree projectTree, MetricFinder metricFinder,
-    ScanGraph graph, DeprecatedViolations deprecatedViolations, ResourceKeyMigration migration) {
+    ScanGraph graph, DeprecatedViolations deprecatedViolations, ResourceKeyMigration migration, MeasureCache measureCache) {
     this.persistence = persistence;
     this.projectTree = projectTree;
     this.metricFinder = metricFinder;
     this.graph = graph;
     this.deprecatedViolations = deprecatedViolations;
     this.migration = migration;
+    this.measureCache = measureCache;
   }
 
   public void start() {
@@ -174,24 +177,20 @@ public class DefaultIndex extends SonarIndex {
 
   @Override
   public Measure getMeasure(Resource resource, Metric metric) {
-    Bucket bucket = buckets.get(resource);
-    if (bucket != null) {
-      Measure measure = bucket.getMeasures(MeasuresFilters.metric(metric));
-      if (measure != null) {
-        return persistence.reloadMeasure(measure);
-      }
-    }
-    return null;
+    return getMeasures(resource, MeasuresFilters.metric(metric));
   }
 
   @Override
   public <M> M getMeasures(Resource resource, MeasuresFilter<M> filter) {
-    Bucket bucket = buckets.get(resource);
-    if (bucket != null) {
-      // TODO the data measures which are not kept in memory are not reloaded yet. Use getMeasure().
-      return bucket.getMeasures(filter);
+    // Reload resource so that effective key is populated
+    Resource indexedResource = getResource(resource);
+    Iterable<Measure> unfiltered;
+    if (filter instanceof MeasuresFilters.MetricFilter) {
+      unfiltered = measureCache.byMetric(indexedResource, ((MeasuresFilters.MetricFilter) filter).filterOnMetricKey());
+    } else {
+      unfiltered = measureCache.byResource(indexedResource);
     }
-    return null;
+    return filter.filter(unfiltered);
   }
 
   /**
@@ -206,11 +205,7 @@ public class DefaultIndex extends SonarIndex {
         throw new SonarException("Unknown metric: " + measure.getMetricKey());
       }
       measure.setMetric(metric);
-      bucket.addMeasure(measure);
-
-      if (measure.getPersistenceMode().useDatabase()) {
-        persistence.saveMeasure(bucket.getResource(), measure);
-      }
+      measureCache.put(resource, measure);
     }
     return measure;
   }
index 14da97381cff207c3e47eb17f3dda2427bab5a82..39b9d8d472a32e7d7a6c622a906f9507832e6e42 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.batch.index;
 import org.sonar.api.batch.Event;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.design.Dependency;
-import org.sonar.api.measures.Measure;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.ProjectLink;
 import org.sonar.api.resources.Resource;
@@ -34,17 +33,14 @@ public final class DefaultPersistenceManager implements PersistenceManager {
 
   private ResourcePersister resourcePersister;
   private SourcePersister sourcePersister;
-  private MeasurePersister measurePersister;
   private DependencyPersister dependencyPersister;
   private LinkPersister linkPersister;
   private EventPersister eventPersister;
 
   public DefaultPersistenceManager(ResourcePersister resourcePersister, SourcePersister sourcePersister,
-                                   MeasurePersister measurePersister, DependencyPersister dependencyPersister,
-                                   LinkPersister linkPersister, EventPersister eventPersister) {
+    DependencyPersister dependencyPersister, LinkPersister linkPersister, EventPersister eventPersister) {
     this.resourcePersister = resourcePersister;
     this.sourcePersister = sourcePersister;
-    this.measurePersister = measurePersister;
     this.dependencyPersister = dependencyPersister;
     this.linkPersister = linkPersister;
     this.eventPersister = eventPersister;
@@ -55,14 +51,6 @@ public final class DefaultPersistenceManager implements PersistenceManager {
     sourcePersister.clear();
   }
 
-  public void setDelayedMode(boolean b) {
-    measurePersister.setDelayedMode(b);
-  }
-
-  public void dump() {
-    measurePersister.dump();
-  }
-
   public void saveProject(Project project, Project parent) {
     resourcePersister.saveProject(project, parent);
   }
@@ -82,16 +70,6 @@ public final class DefaultPersistenceManager implements PersistenceManager {
     return sourcePersister.getSource(resource);
   }
 
-  public void saveMeasure(Resource resource, Measure measure) {
-    if (ResourceUtils.isPersistable(resource)) {
-      measurePersister.saveMeasure(resource, measure);
-    }
-  }
-
-  public Measure reloadMeasure(Measure measure) {
-    return measurePersister.reloadMeasure(measure);
-  }
-
   public void saveDependency(Project project, Dependency dependency, Dependency parentDependency) {
     if (ResourceUtils.isPersistable(dependency.getFrom()) && ResourceUtils.isPersistable(dependency.getTo())) {
       dependencyPersister.saveDependency(project, dependency, parentDependency);
index 8e61a931bd0da86145e2aab288c86ba77a8c7fab..80cac3ab4cb2ee7b398df4926a6b488a60ba5130 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;
 
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-public final class MeasurePersister {
+public final class MeasurePersister implements ScanPersister {
   private final MyBatis mybatis;
-  private final ResourcePersister resourcePersister;
   private final RuleFinder ruleFinder;
-  private final MemoryOptimizer memoryOptimizer;
-  private final SetMultimap<Resource, Measure> unsavedMeasuresByResource = LinkedHashMultimap.create();
-  private boolean delayedMode = false;
+  private final MeasureCache measureCache;
+  private final SnapshotCache snapshotCache;
+  private final ResourceCache resourceCache;
 
-  public MeasurePersister(MyBatis mybatis, ResourcePersister resourcePersister, RuleFinder ruleFinder, MemoryOptimizer memoryOptimizer) {
+  public MeasurePersister(MyBatis mybatis, RuleFinder ruleFinder,
+    MeasureCache measureCache, SnapshotCache snapshotCache, ResourceCache resourceCache) {
     this.mybatis = mybatis;
-    this.resourcePersister = resourcePersister;
     this.ruleFinder = ruleFinder;
-    this.memoryOptimizer = memoryOptimizer;
-  }
-
-  public void setDelayedMode(boolean delayedMode) {
-    this.delayedMode = delayedMode;
+    this.measureCache = measureCache;
+    this.snapshotCache = snapshotCache;
+    this.resourceCache = resourceCache;
   }
 
-  public Measure reloadMeasure(Measure measure) {
-    return memoryOptimizer.reloadMeasure(measure);
-  }
-
-  public void dump() {
-    LoggerFactory.getLogger(getClass()).debug("{} measures to dump", unsavedMeasuresByResource.size());
+  @Override
+  public void persist() {
+    SqlSession session = mybatis.openSession();
+    try {
+      MeasureMapper mapper = session.getMapper(MeasureMapper.class);
 
-    insert(getMeasuresToSave());
-  }
+      for (Entry<Measure> entry : measureCache.entries()) {
+        String effectiveKey = entry.key()[0].toString();
+        Measure measure = entry.value();
+        Resource resource = resourceCache.get(effectiveKey);
 
-  public void saveMeasure(Resource resource, Measure measure) {
-    if (shouldSaveLater(measure)) {
-      if (measure.getMetric().equals(CoreMetrics.TESTS) && unsavedMeasuresByResource.get(resource).contains(measure)) {
-        // Hack for SONAR-5212
-        unsavedMeasuresByResource.remove(resource, measure);
+        if (shouldPersistMeasure(resource, measure)) {
+          Snapshot snapshot = snapshotCache.get(effectiveKey);
+          MeasureModel measureModel = model(measure).setSnapshotId(snapshot.getId());
+          try {
+            mapper.insert(measureModel);
+            if (measureModel.getMeasureData() != null) {
+              mapper.insertData(measureModel.getMeasureData());
+            }
+          } catch (Exception e) {
+            // SONAR-4066
+            throw new SonarException(String.format("Unable to save measure for metric [%s] on component [%s]", measure.getMetricKey(), resource.getKey()), e);
+          }
+        }
       }
-      unsavedMeasuresByResource.put(resource, measure);
-      return;
-    }
-    MeasureModel model;
-    try {
-      model = insertOrUpdate(resource, measure);
-    } catch (Exception e) {
-      // SONAR-4066
-      throw new SonarException(String.format("Unable to save measure for metric [%s] on component [%s]", measure.getMetricKey(), resource.getKey()), e);
-    }
-    if (model != null) {
-      memoryOptimizer.evictDataMeasure(measure, model);
-    }
-  }
 
-  private MeasureModel insertOrUpdate(Resource resource, Measure measure) {
-    Snapshot snapshot = resourcePersister.getSnapshotOrFail(resource);
-    if (measure.getId() != null) {
-      return update(measure, snapshot);
-    }
-    if (shouldPersistMeasure(resource, measure)) {
-      MeasureModel insert = insert(measure, snapshot);
-      measure.setId(insert.getId());
-      return insert;
+      session.commit();
+    } finally {
+      MyBatis.closeQuietly(session);
     }
-    return null;
-  }
-
-  private boolean shouldSaveLater(Measure measure) {
-    return delayedMode && measure.getPersistenceMode().useMemory();
   }
 
   @VisibleForTesting
@@ -125,24 +100,6 @@ public final class MeasurePersister {
       || isNotEmpty;
   }
 
-  private List<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
@@ -172,9 +129,9 @@ public final class MeasurePersister {
     if (measure instanceof RuleMeasure) {
       RuleMeasure ruleMeasure = (RuleMeasure) measure;
       model.setRulePriority(ruleMeasure.getSeverity());
-      Rule rule = ruleMeasure.getRule();
-      if (rule != null) {
-        Rule ruleWithId = ruleFinder.findByKey(rule.getRepositoryKey(), rule.getKey());
+      RuleKey ruleKey = ruleMeasure.ruleKey();
+      if (ruleKey != null) {
+        Rule ruleWithId = ruleFinder.findByKey(ruleKey);
         if (ruleWithId == null) {
           throw new SonarException("Can not save a measure with unknown rule " + ruleMeasure);
         }
@@ -184,95 +141,4 @@ public final class MeasurePersister {
     return model;
   }
 
-  private void insert(Iterable<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
deleted file mode 100644 (file)
index 9daa941..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.index;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.batch.events.DecoratorExecutionHandler;
-import org.sonar.api.batch.events.DecoratorsPhaseHandler;
-import org.sonar.api.batch.events.SensorExecutionHandler;
-import org.sonar.api.database.DatabaseSession;
-import org.sonar.api.database.model.MeasureData;
-import org.sonar.api.database.model.MeasureModel;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.PersistenceMode;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * @since 2.7
- */
-public class MemoryOptimizer implements SensorExecutionHandler, DecoratorExecutionHandler, DecoratorsPhaseHandler {
-
-  private static final Logger LOG = LoggerFactory.getLogger(MemoryOptimizer.class);
-
-  private List<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 d42e1a9f2e18d71086e9f821d85719e46ffc3814..d90f63d8b666dfbba8d18d6c9b029a4e98d9aa69 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.batch.index;
 import org.sonar.api.batch.Event;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.design.Dependency;
-import org.sonar.api.measures.Measure;
 import org.sonar.api.resources.Project;
 import org.sonar.api.resources.ProjectLink;
 import org.sonar.api.resources.Resource;
@@ -32,10 +31,6 @@ import java.util.List;
 public interface PersistenceManager {
   void clear();
 
-  void setDelayedMode(boolean b);
-
-  void dump();
-
   void saveProject(Project project, Project parent);
 
   Snapshot saveResource(Project project, Resource resource, Resource parent);
@@ -44,10 +39,6 @@ public interface PersistenceManager {
 
   String getSource(Resource resource);
 
-  void saveMeasure(Resource resource, Measure measure);
-
-  Measure reloadMeasure(Measure measure);
-
   void saveDependency(Project project, Dependency dependency, Dependency parentDependency);
 
   void saveLink(Project project, ProjectLink link);
index 0ba16ea66d78cb223b9eb7f147b231d4645d3d3a..90bafa7dc3e009577dcc21701a284ae39cd34a02 100644 (file)
@@ -116,8 +116,6 @@ public final class PhaseExecutor {
 
     executeInitializersPhase();
 
-    persistenceManager.setDelayedMode(true);
-
     if (phases.isEnabled(Phases.Phase.SENSOR)) {
       // Index and lock the filesystem
       fs.index();
@@ -135,12 +133,6 @@ public final class PhaseExecutor {
       decoratorsExecutor.execute();
     }
 
-    String saveMeasures = "Save measures";
-    eventBus.fireEvent(new BatchStepEvent(saveMeasures, true));
-    persistenceManager.dump();
-    eventBus.fireEvent(new BatchStepEvent(saveMeasures, false));
-    persistenceManager.setDelayedMode(false);
-
     if (module.isRoot()) {
       jsonReport.execute();
 
@@ -162,6 +154,7 @@ public final class PhaseExecutor {
       LOGGER.debug("Execute {}", persister.getClass().getName());
       persister.persist();
     }
+
     eventBus.fireEvent(new BatchStepEvent(persistersStep, false));
   }
 
index e4eacf6aa8d2aebc440b10e1e1432767beaddfca..335884d7f8779e59058c5a888820ab5ca1f104d3 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.batch.scan;
 
 import com.google.common.annotations.VisibleForTesting;
 import org.sonar.api.BatchComponent;
-import org.sonar.api.BatchExtension;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.batch.InstantiationStrategy;
 import org.sonar.api.batch.bootstrap.ProjectBootstrapper;
@@ -36,18 +35,40 @@ import org.sonar.batch.DefaultFileLinesContextFactory;
 import org.sonar.batch.DefaultResourceCreationLock;
 import org.sonar.batch.ProjectConfigurator;
 import org.sonar.batch.ProjectTree;
-import org.sonar.batch.bootstrap.*;
+import org.sonar.batch.bootstrap.BootstrapSettings;
+import org.sonar.batch.bootstrap.ExtensionInstaller;
+import org.sonar.batch.bootstrap.ExtensionMatcher;
+import org.sonar.batch.bootstrap.ExtensionUtils;
+import org.sonar.batch.bootstrap.MetricProvider;
 import org.sonar.batch.components.PeriodsDefinition;
 import org.sonar.batch.debt.DebtModelProvider;
 import org.sonar.batch.debt.IssueChangelogDebtCalculator;
-import org.sonar.batch.index.*;
-import org.sonar.batch.issue.*;
+import org.sonar.batch.index.Caches;
+import org.sonar.batch.index.ComponentDataCache;
+import org.sonar.batch.index.ComponentDataPersister;
+import org.sonar.batch.index.DefaultIndex;
+import org.sonar.batch.index.DefaultPersistenceManager;
+import org.sonar.batch.index.DefaultResourcePersister;
+import org.sonar.batch.index.DependencyPersister;
+import org.sonar.batch.index.EventPersister;
+import org.sonar.batch.index.LinkPersister;
+import org.sonar.batch.index.MeasurePersister;
+import org.sonar.batch.index.ResourceCache;
+import org.sonar.batch.index.ResourceKeyMigration;
+import org.sonar.batch.index.SnapshotCache;
+import org.sonar.batch.index.SourcePersister;
+import org.sonar.batch.issue.DefaultProjectIssues;
+import org.sonar.batch.issue.DeprecatedViolations;
+import org.sonar.batch.issue.IssueCache;
+import org.sonar.batch.issue.IssuePersister;
+import org.sonar.batch.issue.ScanIssueStorage;
 import org.sonar.batch.phases.GraphPersister;
 import org.sonar.batch.profiling.PhasesSumUpTimeProfiler;
 import org.sonar.batch.rule.RulesProvider;
 import org.sonar.batch.scan.filesystem.InputFileCache;
 import org.sonar.batch.scan.maven.FakeMavenPluginExecutor;
 import org.sonar.batch.scan.maven.MavenPluginExecutor;
+import org.sonar.batch.scan.measure.MeasureCache;
 import org.sonar.batch.source.HighlightableBuilder;
 import org.sonar.batch.source.SymbolizableBuilder;
 import org.sonar.core.component.ScanGraph;
@@ -112,7 +133,6 @@ public class ProjectScanContainer extends ComponentContainer {
       EventPersister.class,
       LinkPersister.class,
       MeasurePersister.class,
-      MemoryOptimizer.class,
       DefaultResourcePersister.class,
       SourcePersister.class,
       DefaultNotificationManager.class,
@@ -169,6 +189,9 @@ public class ProjectScanContainer extends ComponentContainer {
       // Differential periods
       PeriodsDefinition.class,
 
+      // Measures
+      MeasureCache.class,
+
       ProjectSettingsReady.class);
   }
 
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/MeasureCache.java
new file mode 100644 (file)
index 0000000..1f66861
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.scan.measure;
+
+import com.google.common.base.Preconditions;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Resource;
+import org.sonar.batch.index.Cache;
+import org.sonar.batch.index.Cache.Entry;
+import org.sonar.batch.index.Caches;
+
+/**
+ * Cache of all measures. This cache is shared amongst all project modules.
+ */
+public class MeasureCache implements BatchComponent {
+
+  private final Cache<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.allValues(r.getEffectiveKey());
+  }
+
+  public MeasureCache put(Resource resource, Measure measure) {
+    Preconditions.checkNotNull(resource.getEffectiveKey());
+    Preconditions.checkNotNull(measure.getMetricKey());
+    cache.put(resource.getEffectiveKey(), measure.getMetricKey(), measure.hashCode(), measure);
+    return this;
+  }
+
+  public Iterable<Measure> byMetric(Resource resource, String metricKey) {
+    Preconditions.checkNotNull(resource.getEffectiveKey());
+    Preconditions.checkNotNull(metricKey);
+    return cache.values(resource.getEffectiveKey(), metricKey);
+  }
+}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java b/sonar-batch/src/main/java/org/sonar/batch/scan/measure/package-info.java
new file mode 100644 (file)
index 0000000..64f9c87
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+/**
+ * This package is a part of bootstrap process, so we should take care about backward compatibility.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.batch.scan.measure;
+
+import javax.annotation.ParametersAreNonnullByDefault;
index fc82ece746c3539c71bfad28deff0fbe2ad5d316..6edbbab8fbf992941b99a199f173fab3a35929fa 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.Mockito.argThat;
+import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -204,7 +204,7 @@ public class DebtDecoratorTest {
       new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
         org.sonar.api.rules.Rule.create(ruleKey1.repository(), ruleKey1.rule()), null, null)
         .setValue(5d * ONE_DAY_IN_MINUTES)
-    ));
+      ));
     decorator.decorate(resource, context);
 
     verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 7d * ONE_DAY_IN_MINUTES);
@@ -225,7 +225,7 @@ public class DebtDecoratorTest {
       new RuleMeasure(CoreMetrics.TECHNICAL_DEBT,
         org.sonar.api.rules.Rule.create(ruleKey2.repository(), ruleKey2.rule())
         , null, null).setValue(10d * ONE_DAY_IN_MINUTES)
-    ));
+      ));
     decorator.decorate(resource, context);
 
     verify(context).saveMeasure(CoreMetrics.TECHNICAL_DEBT, 15d * ONE_DAY_IN_MINUTES);
@@ -319,8 +319,7 @@ public class DebtDecoratorTest {
       description.appendText(new StringBuilder()
         .append("value=").append(value).append(",")
         .append("characteristic=").append(characteristic.key()).append(",")
-        .append("metric=").append(metric.getKey()).toString())
-      ;
+        .append("metric=").append(metric.getKey()).toString());
     }
   }
 
@@ -342,8 +341,7 @@ public class DebtDecoratorTest {
       }
       RuleMeasure m = (RuleMeasure) o;
       return ObjectUtils.equals(metric, m.getMetric()) &&
-        ObjectUtils.equals(ruleKey.repository(), m.getRule().getRepositoryKey()) &&
-        ObjectUtils.equals(ruleKey.rule(), m.getRule().getKey()) &&
+        ObjectUtils.equals(ruleKey, m.ruleKey()) &&
         ObjectUtils.equals(value, m.getValue());
     }
 
index 2fc521a64d5e242a8582f5a02a58047b2036f454..cb20f15cde621d3f1841cbbd809d95b509a58d8a 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;
@@ -51,42 +48,6 @@ public class BucketTest {
     assertThat(packageBucket.getChildren(), hasItem(fileBucket));
   }
 
-  @Test
-  public void shouldAddNewMeasure() {
-    Bucket fileBucket = new Bucket(javaFile);
-    Measure measure = new Measure(ncloc).setValue(1200.0);
-    fileBucket.addMeasure(measure);
-
-    assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1));
-    assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)), is(measure));
-  }
-
-  @Test
-  public void shouldUpdateMeasure() {
-    Bucket fileBucket = new Bucket(javaFile);
-    Measure measure = new Measure(ncloc).setValue(1200.0);
-    fileBucket.addMeasure(measure);
-
-    assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1));
-    assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)).getValue(), is(1200.0));
-
-    measure.setValue(500.0);
-    fileBucket.addMeasure(measure);
-
-    assertThat(fileBucket.getMeasures(MeasuresFilters.all()).size(), is(1));
-    assertThat(fileBucket.getMeasures(MeasuresFilters.metric(ncloc)).getValue(), is(500.0));
-  }
-
-  @Test(expected = SonarException.class)
-  public void shouldFailIfAddingSameMeasures() {
-    Bucket fileBucket = new Bucket(javaFile);
-    Measure measure = new Measure(ncloc).setValue(1200.0);
-    fileBucket.addMeasure(measure);
-
-    measure = new Measure(ncloc).setValue(500.0);
-    fileBucket.addMeasure(measure);
-  }
-
   @Test
   public void shouldBeEquals() {
     assertEquals(new Bucket(directory), new Bucket(directory));
index 4abc71694cf1ffc6c24fa9c1bbbe89fb1b96a349..9a162fbf42549f58af29057a4431a2cb8b037a6b 100644 (file)
@@ -28,7 +28,13 @@ import org.sonar.api.measures.Measure;
 import org.sonar.api.measures.MeasuresFilters;
 import org.sonar.api.measures.MetricFinder;
 import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.resources.*;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.Library;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.rules.Violation;
@@ -36,6 +42,7 @@ import org.sonar.api.violations.ViolationQuery;
 import org.sonar.batch.ProjectTree;
 import org.sonar.batch.issue.DeprecatedViolations;
 import org.sonar.batch.issue.ModuleIssues;
+import org.sonar.batch.scan.measure.MeasureCache;
 import org.sonar.core.component.ScanGraph;
 
 import java.io.IOException;
@@ -68,7 +75,8 @@ public class DefaultIndexTest {
     ruleFinder = mock(RuleFinder.class);
 
     ProjectTree projectTree = mock(ProjectTree.class);
-    index = new DefaultIndex(mock(PersistenceManager.class), projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, mock(ResourceKeyMigration.class));
+    index = new DefaultIndex(mock(PersistenceManager.class), projectTree, metricFinder, mock(ScanGraph.class), deprecatedViolations, mock(ResourceKeyMigration.class),
+      mock(MeasureCache.class));
 
     java.io.File baseDir = temp.newFolder();
     project = new Project("project");
index fc3a69628d28a6cb26a0bb2985096d9f05e462fa..9785c944b7a85703e360e8153c51643d8fac8dd0 100644 (file)
@@ -23,8 +23,6 @@ import org.apache.commons.lang.StringUtils;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
-import org.mockito.ArgumentCaptor;
-import org.sonar.api.database.model.MeasureModel;
 import org.sonar.api.database.model.Snapshot;
 import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.measures.Measure;
@@ -38,13 +36,13 @@ import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleFinder;
 import org.sonar.api.rules.RulePriority;
 import org.sonar.api.utils.SonarException;
+import org.sonar.batch.scan.measure.MeasureCache;
 import org.sonar.core.persistence.AbstractDaoTestCase;
 
+import java.util.Arrays;
+
 import static org.fest.assertions.Assertions.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class MeasurePersisterTest extends AbstractDaoTestCase {
@@ -63,22 +61,28 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
 
   MeasurePersister measurePersister;
   RuleFinder ruleFinder = mock(RuleFinder.class);
-  ResourcePersister resourcePersister = mock(ResourcePersister.class);
-  MemoryOptimizer memoryOptimizer = mock(MemoryOptimizer.class);
   Project project = new Project("foo");
   Directory aDirectory = new Directory("org/foo");
   File aFile = new File("org/foo/Bar.java");
   Snapshot projectSnapshot = snapshot(PROJECT_SNAPSHOT_ID);
   Snapshot packageSnapshot = snapshot(PACKAGE_SNAPSHOT_ID);
 
+  private SnapshotCache snapshotCache;
+
+  private MeasureCache measureCache;
+
   @Before
   public void mockResourcePersister() {
-    when(resourcePersister.getSnapshotOrFail(project)).thenReturn(projectSnapshot);
-    when(resourcePersister.getSnapshotOrFail(aDirectory)).thenReturn(packageSnapshot);
-    when(resourcePersister.getSnapshot(project)).thenReturn(projectSnapshot);
-    when(resourcePersister.getSnapshot(aDirectory)).thenReturn(packageSnapshot);
+    snapshotCache = mock(SnapshotCache.class);
+    measureCache = mock(MeasureCache.class);
+    ResourceCache resourceCache = mock(ResourceCache.class);
+    when(snapshotCache.get("foo")).thenReturn(projectSnapshot);
+    when(snapshotCache.get("foo:org/foo")).thenReturn(packageSnapshot);
+    when(resourceCache.get("foo")).thenReturn(project);
+    when(resourceCache.get("foo:org/foo/Bar.java")).thenReturn(aFile);
+    when(resourceCache.get("foo:org/foo")).thenReturn(aDirectory);
 
-    measurePersister = new MeasurePersister(getMyBatis(), resourcePersister, ruleFinder, memoryOptimizer);
+    measurePersister = new MeasurePersister(getMyBatis(), ruleFinder, measureCache, snapshotCache, resourceCache);
   }
 
   @Test
@@ -86,11 +90,10 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
     setupData("empty");
 
     Measure measure = new Measure(ncloc()).setValue(1234.0);
-    measurePersister.saveMeasure(project, measure);
+    when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry<Measure>(new String[] {"foo", "ncloc"}, measure)));
+    measurePersister.persist();
 
     checkTables("shouldInsertMeasure", "project_measures");
-    verify(memoryOptimizer).evictDataMeasure(eq(measure), any(MeasureModel.class));
-    assertThat(measure.getId()).isNotNull();
   }
 
   @Test
@@ -98,20 +101,12 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
     setupData("empty");
 
     Measure measure = new Measure(ncloc()).setValue(1234.0).setAlertText(TOO_LONG);
+    when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry<Measure>(new String[] {"foo", "ncloc"}, measure)));
 
     thrown.expect(SonarException.class);
     thrown.expectMessage("Unable to save measure for metric [ncloc] on component [foo]");
 
-    measurePersister.saveMeasure(project, measure);
-  }
-
-  @Test
-  public void should_reload_measure() {
-    Measure measure = new Measure(ncloc());
-
-    measurePersister.reloadMeasure(measure);
-
-    verify(memoryOptimizer).reloadMeasure(measure);
+    measurePersister.persist();
   }
 
   @Test
@@ -122,10 +117,11 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
     when(ruleFinder.findByKey("pmd", "key")).thenReturn(rule);
 
     Measure measure = new RuleMeasure(ncloc(), rule, RulePriority.MAJOR, 1).setValue(1234.0);
-    measurePersister.saveMeasure(project, measure);
+    when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry<Measure>(new String[] {"foo", "ncloc"}, measure)));
+
+    measurePersister.persist();
 
     checkTables("shouldInsertRuleMeasure", "project_measures");
-    assertThat(measure.getId()).isNotNull();
   }
 
   @Test
@@ -133,21 +129,21 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
     setupData("empty");
 
     Measure withLargeData = new Measure(ncloc()).setData(LONG);
-    measurePersister.saveMeasure(project, withLargeData);
+    when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry<Measure>(new String[] {"foo", "ncloc"}, withLargeData)));
 
-    checkTables("shouldInsertMeasureWithLargeData", "project_measures", "measure_data");
+    measurePersister.persist();
 
-    ArgumentCaptor<MeasureModel> validMeasureModel = ArgumentCaptor.forClass(MeasureModel.class);
-    verify(memoryOptimizer).evictDataMeasure(eq(withLargeData), validMeasureModel.capture());
-    assertThat(validMeasureModel.getValue().getMeasureData().getId()).isNotNull();
-    assertThat(withLargeData.getId()).isNotNull();
+    checkTables("shouldInsertMeasureWithLargeData", "project_measures", "measure_data");
   }
 
   @Test
   public void should_not_save_best_values() {
     setupData("empty");
 
-    measurePersister.saveMeasure(aFile, new Measure(coverage()).setValue(100.0));
+    Measure measure = new Measure(coverage()).setValue(100.0);
+    when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry<Measure>(new String[] {"foo:org/foo/Bar.java", "coverage"}, measure)));
+
+    measurePersister.persist();
 
     assertEmptyTables("project_measures", "measure_data");
   }
@@ -156,7 +152,10 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
   public void should_not_save_memory_only_measures() {
     setupData("empty");
 
-    measurePersister.saveMeasure(aFile, new Measure("ncloc").setPersistenceMode(PersistenceMode.MEMORY));
+    Measure measure = new Measure("ncloc").setPersistenceMode(PersistenceMode.MEMORY);
+    when(measureCache.entries()).thenReturn(Arrays.asList(new Cache.Entry<Measure>(new String[] {"foo:org/foo/Bar.java", "ncloc"}, measure)));
+
+    measurePersister.persist();
 
     assertEmptyTables("project_measures", "measure_data");
   }
@@ -165,88 +164,15 @@ public class MeasurePersisterTest extends AbstractDaoTestCase {
   public void should_always_save_non_file_measures() {
     setupData("empty");
 
-    measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(200.0));
-    measurePersister.saveMeasure(aDirectory, new Measure(ncloc()).setValue(300.0));
-
-    checkTables("shouldAlwaysPersistNonFileMeasures", "project_measures");
-  }
-
-  @Test
-  public void should_update_measure() {
-    setupData("data");
-
-    measurePersister.saveMeasure(project, new Measure(coverage()).setValue(12.5).setId(1L));
-    measurePersister.saveMeasure(project, new Measure(coverage()).setData(SHORT).setId(2L));
-    measurePersister.saveMeasure(aDirectory, new Measure(coverage()).setData(LONG).setId(3L));
-
-    checkTables("shouldUpdateMeasure", "project_measures", "measure_data");
-  }
-
-  @Test
-  public void should_add_delayed_measure_several_times() {
-    setupData("empty");
-
-    Measure measure = new Measure(ncloc());
-
-    measurePersister.setDelayedMode(true);
-    measurePersister.saveMeasure(project, measure.setValue(200.0));
-    measurePersister.saveMeasure(project, measure.setValue(300.0));
-    measurePersister.dump();
-
-    checkTables("shouldAddDelayedMeasureSeveralTimes", "project_measures");
-  }
-
-  @Test
-  public void should_delay_saving() {
-    setupData("empty");
-
-    measurePersister.setDelayedMode(true);
-    measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setData(SHORT));
-    measurePersister.saveMeasure(aDirectory, new Measure(ncloc()).setValue(50.0).setData(LONG));
-
-    assertEmptyTables("project_measures");
-
-    measurePersister.dump();
-    checkTables("shouldDelaySaving", "project_measures", "measure_data");
-  }
-
-  @Test
-  public void should_display_contextual_info_when_error_during_delay_saving() {
-    setupData("empty");
-
-    measurePersister.setDelayedMode(true);
-
-    measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setData(SHORT).setAlertText(TOO_LONG));
-
-    thrown.expect(SonarException.class);
-    thrown.expectMessage("Unable to save measure for metric [ncloc] on component [foo]");
-
-    measurePersister.dump();
-  }
-
-  @Test
-  public void should_not_delay_saving_with_database_only_measure() {
-    setupData("empty");
-
-    measurePersister.setDelayedMode(true);
-    measurePersister.saveMeasure(project, new Measure(ncloc()).setValue(1234.0).setPersistenceMode(PersistenceMode.DATABASE));
-    measurePersister.saveMeasure(aDirectory, new Measure(ncloc()).setValue(50.0));
+    Measure measure1 = new Measure(ncloc()).setValue(200.0);
+    Measure measure2 = new Measure(ncloc()).setValue(300.0);
+    when(measureCache.entries()).thenReturn(Arrays.asList(
+      new Cache.Entry<Measure>(new String[] {"foo", "ncloc"}, measure1),
+      new Cache.Entry<Measure>(new String[] {"foo:org/foo", "ncloc"}, measure2)));
 
-    checkTables("shouldInsertMeasure", "project_measures");
-  }
+    measurePersister.persist();
 
-  @Test
-  public void should_not_save_best_value_measures_in_delayed_mode() {
-    setupData("empty");
-
-    measurePersister.setDelayedMode(true);
-    measurePersister.saveMeasure(aFile, new Measure(coverage()).setValue(100.0));
-
-    assertEmptyTables("project_measures", "measure_data");
-
-    measurePersister.dump();
-
-    assertEmptyTables("project_measures", "measure_data");
+    checkTables("shouldAlwaysPersistNonFileMeasures", "project_measures");
   }
 
   @Test
diff --git a/sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java b/sonar-batch/src/test/java/org/sonar/batch/index/MemoryOptimizerTest.java
deleted file mode 100644 (file)
index 468654d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.batch.index;
-
-import org.junit.Test;
-import org.sonar.api.database.model.MeasureData;
-import org.sonar.api.database.model.MeasureModel;
-import org.sonar.api.measures.CoreMetrics;
-import org.sonar.api.measures.Measure;
-import org.sonar.api.measures.PersistenceMode;
-import org.sonar.jpa.test.AbstractDbUnitTestCase;
-
-import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.core.IsNull.nullValue;
-import static org.junit.Assert.assertThat;
-
-public class MemoryOptimizerTest extends AbstractDbUnitTestCase {
-
-  @Test
-  public void shouldEvictDatabaseOnlyMeasure() {
-    MemoryOptimizer optimizer = new MemoryOptimizer(getSession());
-    Measure measure = new Measure(CoreMetrics.CONDITIONS_BY_LINE)
-        .setData("10=23")
-        .setPersistenceMode(PersistenceMode.DATABASE)
-        .setId(12345L);
-    MeasureModel model = newPersistedModel();
-
-    optimizer.evictDataMeasure(measure, model);
-
-    assertThat(optimizer.isTracked(12345L),is(true));
-    assertThat(measure.getData(), nullValue());// data has been removed from memory
-  }
-
-  @Test
-  public void shouldNotEvictStandardMeasure() {
-    MemoryOptimizer optimizer = new MemoryOptimizer(getSession());
-    Measure measure = new Measure(CoreMetrics.PROFILE)
-        .setData("Sonar way")
-        .setId(12345L);
-    MeasureModel model = newPersistedModel();
-
-    optimizer.evictDataMeasure(measure, model);
-
-    assertThat(optimizer.isTracked(12345L),is(false));
-    assertThat(measure.getData(), is("Sonar way"));
-  }
-
-  @Test
-  public void shouldReloadEvictedMeasure() {
-    setupData("shouldReloadEvictedMeasure");
-    MemoryOptimizer optimizer = new MemoryOptimizer(getSession());
-    Measure measure = new Measure(CoreMetrics.CONDITIONS_BY_LINE)
-        .setData("initial")
-        .setPersistenceMode(PersistenceMode.DATABASE)
-        .setId(12345L);
-
-    optimizer.evictDataMeasure(measure, newPersistedModel());
-    assertThat(measure.getData(), nullValue());
-
-    optimizer.reloadMeasure(measure);
-
-    assertThat(measure.getData().length(), greaterThan(5));
-
-    optimizer.flushMemory();
-    assertThat(measure.getData(), nullValue());
-  }
-
-  private MeasureModel newPersistedModel() {
-    MeasureModel model = new MeasureModel();
-    model.setId(12345L);
-    MeasureData measureData = new MeasureData();
-    measureData.setId(500);
-    model.setMeasureData(measureData);
-    return model;
-  }
-}
diff --git a/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java b/sonar-batch/src/test/java/org/sonar/batch/scan/measure/MeasureCacheTest.java
new file mode 100644 (file)
index 0000000..a1791ff
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.batch.scan.measure;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.RuleMeasure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.batch.index.Caches;
+import org.sonar.batch.index.CachesTest;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class MeasureCacheTest {
+
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+
+  Caches caches;
+
+  @Before
+  public void start() throws Exception {
+    caches = CachesTest.createCacheOnTemp(temp);
+    caches.start();
+  }
+
+  @After
+  public void stop() {
+    caches.stop();
+  }
+
+  @Test
+  public void should_add_measure() throws Exception {
+    MeasureCache cache = new MeasureCache(caches);
+    Project p = new Project("struts");
+
+    assertThat(cache.entries()).hasSize(0);
+
+    assertThat(cache.byResource(p)).hasSize(0);
+    assertThat(cache.byMetric(p, "ncloc")).hasSize(0);
+
+    Measure m = new Measure(CoreMetrics.NCLOC, 1.0);
+    cache.put(p, m);
+
+    assertThat(cache.entries()).hasSize(1);
+
+    assertThat(cache.byMetric(p, "ncloc")).hasSize(1);
+    assertThat(cache.byMetric(p, "ncloc").iterator().next()).isEqualTo(m);
+    assertThat(cache.byResource(p)).hasSize(1);
+    assertThat(cache.byResource(p).iterator().next()).isEqualTo(m);
+
+    Measure mRule = RuleMeasure.createForPriority(CoreMetrics.CRITICAL_VIOLATIONS, RulePriority.BLOCKER, 1.0);
+    cache.put(p, mRule);
+
+    assertThat(cache.entries()).hasSize(2);
+
+    assertThat(cache.byResource(p)).hasSize(2);
+    assertThat(cache.byMetric(p, "ncloc")).hasSize(1);
+  }
+}
index 866785feeefc08867da5df78e147e2aea962bb28..4eb61021ca55b4d1d3de6f50bde66d6a05ae6caa 100644 (file)
@@ -30,6 +30,8 @@ import org.sonar.api.rules.Violation;
 import org.sonar.api.violations.ViolationQuery;
 import org.sonar.graph.DirectedGraphAccessor;
 
+import javax.annotation.CheckForNull;
+
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
@@ -118,8 +120,10 @@ public abstract class SonarIndex implements DirectedGraphAccessor<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 e2448adb5da7f0b512150a9b9dfe30039887d05c..5aa343120b6c61655a3190166f59078b01f5348f 100644 (file)
@@ -28,6 +28,7 @@ import org.sonar.api.technicaldebt.batch.Requirement;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
+import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.Date;
@@ -37,7 +38,7 @@ import java.util.Date;
  *
  * @since 1.10
  */
-public class Measure {
+public class Measure implements Serializable {
   private static final String INDEX_SHOULD_BE_IN_RANGE_FROM_1_TO_5 = "Index should be in range from 1 to 5";
 
   protected static final int MAX_TEXT_SIZE = 96;
@@ -47,8 +48,6 @@ public class Measure {
    */
   public static final int DEFAULT_PRECISION = 1;
 
-  // for internal use
-  private Long id;
   protected String metricKey;
   protected Metric metric;
   protected Double value;
@@ -412,24 +411,6 @@ public class Measure {
     return this;
   }
 
-  /**
-   * @return the measure id - Internal use only
-   */
-  public Long getId() {
-    return id;
-  }
-
-  /**
-   * Sets the measure id - Internal use only
-   *
-   * @param id the id
-   * @return the measure object instance
-   */
-  public Measure setId(Long id) {
-    this.id = id;
-    return this;
-  }
-
   /**
    * @return the first variation value
    * @since 2.5
@@ -646,7 +627,7 @@ public class Measure {
     return metric.isOptimizedBestValue() == Boolean.TRUE
       && metric.getBestValue() != null
       && (value == null || NumberUtils.compare(metric.getBestValue(), value) == 0)
-      && allNull(id, alertStatus, description, tendency, url, data)
+      && allNull(alertStatus, description, tendency, url, data)
       && isZeroVariation(variation1, variation2, variation3, variation4, variation5);
   }
 
index e6c0c4912e1f39a186e661349f5e91811949eb4d..c7731a1120a578422e629cadee5757209ad6693d 100644 (file)
  */
 package org.sonar.api.measures;
 
-import java.util.Collection;
 
 /**
  * @since 1.10
  */
 public interface MeasuresFilter<M> {
 
-  M filter(Collection<Measure> measures);
+  M filter(Iterable<Measure> measures);
 
 }
index 4ed4a8ffb1b1512f1662770f65a69687add63085..7a89e5f2030704cc52edf5ada1311e760ded89de 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.api.measures;
 
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.technicaldebt.batch.Characteristic;
 import org.sonar.api.technicaldebt.batch.Requirement;
@@ -37,8 +38,15 @@ public final class MeasuresFilters {
 
   public static MeasuresFilter<Collection<Measure>> all() {
     return new MeasuresFilter<Collection<Measure>>() {
-      public Collection<Measure> filter(Collection<Measure> measures) {
-        return measures;
+      @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;
       }
     };
   }
@@ -49,8 +57,8 @@ public final class MeasuresFilters {
 
   public static MeasuresFilter<Measure> metric(final String metricKey) {
     return new MetricFilter<Measure>(metricKey) {
-
-      public Measure filter(Collection<Measure> measures) {
+      @Override
+      public Measure filter(Iterable<Measure> measures) {
         if (measures == null) {
           return null;
         }
@@ -70,7 +78,8 @@ public final class MeasuresFilters {
   public static MeasuresFilter<Measure> characteristic(final Metric metric, final Characteristic characteristic) {
     return new MetricFilter<Measure>(metric) {
 
-      public Measure filter(Collection<Measure> measures) {
+      @Override
+      public Measure filter(Iterable<Measure> measures) {
         if (measures == null) {
           return null;
         }
@@ -100,7 +109,8 @@ public final class MeasuresFilters {
   public static MeasuresFilter<Measure> requirement(final Metric metric, final Requirement requirement) {
     return new MetricFilter<Measure>(metric) {
 
-      public Measure filter(Collection<Measure> measures) {
+      @Override
+      public Measure filter(Iterable<Measure> measures) {
         if (measures == null) {
           return null;
         }
@@ -128,7 +138,7 @@ public final class MeasuresFilters {
    */
   public static MeasuresFilter<Measure> measure(final Measure measure) {
     return new MeasuresFilter<Measure>() {
-      public Measure filter(Collection<Measure> measures) {
+      public Measure filter(Iterable<Measure> measures) {
         if (measures == null) {
           return null;
         }
@@ -143,7 +153,7 @@ public final class MeasuresFilters {
   }
 
   public static MeasuresFilter<RuleMeasure> rule(final Metric metric, final Rule rule) {
-    return new RuleFilter(metric, rule);
+    return new RuleFilter(metric, rule.ruleKey());
   }
 
   public static MeasuresFilter<Collection<RuleMeasure>> rules(final Metric metric) {
@@ -151,10 +161,11 @@ public final class MeasuresFilters {
 
       private boolean apply(Measure measure) {
         return measure instanceof RuleMeasure && metric.equals(measure.getMetric())
-          && measure.getPersonId() == null && ((RuleMeasure) measure).getRule() != null;
+          && measure.getPersonId() == null && ((RuleMeasure) measure).ruleKey() != null;
       }
 
-      public Collection<RuleMeasure> filter(Collection<Measure> measures) {
+      @Override
+      public Collection<RuleMeasure> filter(Iterable<Measure> measures) {
         if (measures == null) {
           return null;
         }
@@ -202,7 +213,8 @@ public final class MeasuresFilters {
 
     abstract boolean doApply(RuleMeasure ruleMeasure);
 
-    public M filter(Collection<Measure> measures) {
+    @Override
+    public M filter(Iterable<Measure> measures) {
       if (measures == null) {
         return null;
       }
@@ -216,17 +228,17 @@ public final class MeasuresFilters {
   }
 
   private static class RuleFilter extends AbstractRuleMeasureFilter<RuleMeasure> {
-    private Rule rule;
+    private RuleKey ruleKey;
 
-    protected RuleFilter(Metric metric, Rule rule) {
+    protected RuleFilter(Metric metric, RuleKey ruleKey) {
       super(metric);
-      this.rule = rule;
+      this.ruleKey = ruleKey;
     }
 
     @Override
     boolean doApply(RuleMeasure measure) {
-      return measure.getRule() != null
-        && rule.equals(measure.getRule());
+      return measure.ruleKey() != null
+        && ruleKey.equals(measure.ruleKey());
     }
   }
 }
index dc7d507153b78b164850331937ec8eb240ce3d30..bc809c88884f32cd96b292de045654393078e4fb 100644 (file)
@@ -27,7 +27,16 @@ import org.sonar.api.ServerExtension;
 import org.sonar.api.batch.InstantiationStrategy;
 
 import javax.annotation.Nullable;
-import javax.persistence.*;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+
+import java.io.Serializable;
 
 /**
  * This class represents the definition of a metric in Sonar.
@@ -37,7 +46,7 @@ import javax.persistence.*;
 @Table(name = "metrics")
 @Entity(name = "Metric")
 @InstantiationStrategy(InstantiationStrategy.PER_BATCH)
-public class Metric implements ServerExtension, BatchExtension {
+public class Metric implements ServerExtension, BatchExtension, Serializable {
 
   /**
    * A metric bigger value means a degradation
@@ -80,7 +89,7 @@ public class Metric implements ServerExtension, BatchExtension {
   private Integer id;
 
   @Transient
-  private Formula formula;
+  private transient Formula formula;
 
   @Column(name = "name", updatable = false, nullable = false, length = 64)
   private String key;
@@ -206,7 +215,7 @@ public class Metric implements ServerExtension, BatchExtension {
    */
   @Deprecated
   public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, @Nullable String domain,
-      boolean userManaged) {
+    boolean userManaged) {
     this.key = key;
     this.description = description;
     this.type = type;
index b0e1199a5c128f160d039865da1b36e4a67a6365..b2feae04744af4a67a86cab8790da05e65db9242 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.api.measures;
 import org.apache.commons.lang.builder.EqualsBuilder;
 import org.apache.commons.lang.builder.HashCodeBuilder;
 import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RulePriority;
 
@@ -32,24 +33,54 @@ import javax.annotation.Nullable;
  */
 public class RuleMeasure extends Measure {
 
-  private Rule rule;
+  /**
+   * @deprecated since 4.4
+   */
+  @Deprecated
+  private transient Rule rule;
+  private RuleKey ruleKey;
   private RulePriority rulePriority;
 
   /**
    * This constructor is for internal use only. Please use static methods createForXXX().
+   * @deprecated since 4.4 use {@link #RuleMeasure(Metric, RuleKey, RulePriority, Integer)}
    */
+  @Deprecated
   public RuleMeasure(Metric metric, @Nullable Rule rule, @Nullable RulePriority rulePriority, @Nullable Integer ruleCategory) {
-    super(metric);
+    this(metric, rule.ruleKey(), rulePriority, ruleCategory);
     this.rule = rule;
+  }
+
+  public RuleMeasure(Metric metric, @Nullable RuleKey ruleKey, @Nullable RulePriority rulePriority, @Nullable Integer ruleCategory) {
+    super(metric);
+    this.ruleKey = ruleKey;
     this.rulePriority = rulePriority;
   }
 
+  public RuleKey ruleKey() {
+    return ruleKey;
+  }
+
+  public RuleMeasure setRuleKey(RuleKey ruleKey) {
+    this.ruleKey = ruleKey;
+    return this;
+  }
+
+  /**
+   * @deprecated since 4.4 use {@link #ruleKey()}
+   */
+  @Deprecated
   public Rule getRule() {
     return rule;
   }
 
+  /**
+   * @deprecated since 4.4 use {@link #setRuleKey()}
+   */
+  @Deprecated
   public RuleMeasure setRule(Rule rule) {
     this.rule = rule;
+    this.ruleKey = rule.ruleKey();
     return this;
   }
 
@@ -115,10 +146,10 @@ public class RuleMeasure extends Measure {
     }
     RuleMeasure other = (RuleMeasure) obj;
     return new EqualsBuilder()
-        .append(getMetric(), other.getMetric())
-        .append(personId, other.personId)
-        .append(rule, other.rule)
-        .isEquals();
+      .append(getMetric(), other.getMetric())
+      .append(personId, other.personId)
+      .append(ruleKey, other.ruleKey)
+      .isEquals();
   }
 
   @Override
@@ -129,35 +160,42 @@ public class RuleMeasure extends Measure {
   @Override
   public int hashCode() {
     return new HashCodeBuilder(17, 37)
-        .append(getMetric())
-        .append(personId)
-        .append(rule)
-        .toHashCode();
+      .append(getMetric())
+      .append(personId)
+      .append(ruleKey)
+      .toHashCode();
   }
 
   @Override
   public String toString() {
     return new ToStringBuilder(this)
-        .append("id", getId())
-        .append("metric", metric)
-        .append("personId", personId)
-        .append("rule", rule)
-        .append("value", value)
-        .append("data", data)
-        .append("description", description)
-        .append("alertStatus", alertStatus)
-        .append("alertText", alertText)
-        .append("tendency", tendency)
-        .append("severity", rulePriority)
-        .toString();
+      .append("metric", metric)
+      .append("personId", personId)
+      .append("ruleKey", ruleKey)
+      .append("value", value)
+      .append("data", data)
+      .append("description", description)
+      .append("alertStatus", alertStatus)
+      .append("alertText", alertText)
+      .append("tendency", tendency)
+      .append("severity", rulePriority)
+      .toString();
   }
 
+  /**
+   * @deprecated since 4.4 use {@link #createForRule(Metric, RuleKey, Double)}
+   */
+  @Deprecated
   public static RuleMeasure createForRule(Metric metric, Rule rule, @Nullable Double value) {
     return new RuleMeasure(metric, rule, null, null).setValue(value);
   }
 
+  public static RuleMeasure createForRule(Metric metric, RuleKey ruleKey, @Nullable Double value) {
+    return new RuleMeasure(metric, ruleKey, null, null).setValue(value);
+  }
+
   public static RuleMeasure createForPriority(Metric metric, RulePriority priority, @Nullable Double value) {
-    return new RuleMeasure(metric, null, priority, null).setValue(value);
+    return new RuleMeasure(metric, (RuleKey) null, priority, null).setValue(value);
   }
 
   /**
@@ -165,6 +203,6 @@ public class RuleMeasure extends Measure {
    */
   @Deprecated
   public static RuleMeasure createForCategory(Metric metric, Integer category, @Nullable Double value) {
-    return new RuleMeasure(metric, null, null, category).setValue(value);
+    return new RuleMeasure(metric, (RuleKey) null, null, category).setValue(value);
   }
 }
index 39453f963ff39a9d51dda44f7e402a4bf6cfa507..402a5ec9f9d12e53e8cf5a49064174b15d10668e 100644 (file)
@@ -34,7 +34,19 @@ import org.sonar.check.Cardinality;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import javax.persistence.*;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
 
 import java.util.ArrayList;
 import java.util.Date;
@@ -279,15 +291,15 @@ public class Rule {
 
   public RuleParam createParameter() {
     RuleParam parameter = new RuleParam()
-        .setRule(this);
+      .setRule(this);
     params.add(parameter);
     return parameter;
   }
 
   public RuleParam createParameter(String key) {
     RuleParam parameter = new RuleParam()
-        .setKey(key)
-        .setRule(this);
+      .setKey(key)
+      .setRule(this);
     params.add(parameter);
     return parameter;
   }
@@ -468,7 +480,6 @@ public class Rule {
     return this;
   }
 
-
   /**
    * For internal use only.
    *
@@ -499,34 +510,34 @@ public class Rule {
     }
     Rule other = (Rule) obj;
     return new EqualsBuilder()
-        .append(pluginName, other.getRepositoryKey())
-        .append(key, other.getKey())
-        .isEquals();
+      .append(pluginName, other.getRepositoryKey())
+      .append(key, other.getKey())
+      .isEquals();
   }
 
   @Override
   public int hashCode() {
     return new HashCodeBuilder(17, 37)
-        .append(pluginName)
-        .append(key)
-        .toHashCode();
+      .append(pluginName)
+      .append(key)
+      .toHashCode();
   }
 
   @Override
   public String toString() {
     // Note that ReflectionToStringBuilder will not work here - see SONAR-3077
     return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
-        .append("id", id)
-        .append("name", name)
-        .append("key", key)
-        .append("configKey", configKey)
-        .append("plugin", pluginName)
-        .append("severity", priority)
-        .append("cardinality", cardinality)
-        .append("status", status)
-        .append("language", language)
-        .append("parent", parent)
-        .toString();
+      .append("id", id)
+      .append("name", name)
+      .append("key", key)
+      .append("configKey", configKey)
+      .append("plugin", pluginName)
+      .append("severity", priority)
+      .append("cardinality", cardinality)
+      .append("status", status)
+      .append("language", language)
+      .append("parent", parent)
+      .toString();
   }
 
   @CheckForNull
index fadad455575287ea637f92684e01394927319260..b649a4e466acacbfdffa8bc94cc37531dadbffb7 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.api.technicaldebt.batch;
 
 import javax.annotation.CheckForNull;
 
+import java.io.Serializable;
 import java.util.Date;
 import java.util.List;
 
@@ -30,7 +31,7 @@ import java.util.List;
  * @deprecated since 4.3
  */
 @Deprecated
-public interface Characteristic {
+public interface Characteristic extends Serializable {
 
   Integer id();
 
index 606d2a3fb1822ea65cec7c25e34c3c6d600fdea3..71204e9ffb837dac9eb4947825614c36614f1854 100644 (file)
@@ -24,6 +24,7 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.utils.WorkUnit;
 import org.sonar.api.utils.internal.WorkDuration;
 
+import java.io.Serializable;
 import java.util.Date;
 
 /**
@@ -31,7 +32,7 @@ import java.util.Date;
  * @deprecated since 4.3
  */
 @Deprecated
-public interface Requirement {
+public interface Requirement extends Serializable {
 
   Integer id();
 
index f9f569f199b55e4d1c36027c9b558dbc9e538f5c..ea075ed2a9900a37623fc787658a7a81ec42b63c 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.rules.RulePriority;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 
 import static org.hamcrest.core.Is.is;
@@ -37,8 +38,8 @@ public class MeasuresFiltersTest {
     MeasuresFilter<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));
   }
@@ -46,10 +47,13 @@ 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));
 
-    assertThat(MeasuresFilters.all().filter(measures).size(), is(2));
+    Iterator<Measure> filteredMeasures = MeasuresFilters.all().filter(measures).iterator();
+    filteredMeasures.next();
+    filteredMeasures.next();
+    assertThat(filteredMeasures.hasNext(), is(false));
   }
 
   @Test
@@ -58,13 +62,13 @@ public class MeasuresFiltersTest {
     Rule rule2 = new Rule("pmd", "key2");
     MeasuresFilter<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));
   }
@@ -75,13 +79,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));
   }
@@ -90,10 +94,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 091d8c781cd9830fd55f6bb8596947d25b2d07de..3f9f67ce23645ed2b8bd27a9128662cf0bc0f5d2 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, m.getRule()) &&
+      ObjectUtils.equals(rule.ruleKey(), m.ruleKey()) &&
       NumberUtils.compare(value, m.getValue()) == 0;
   }
 }