aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-05-07 11:37:06 +0200
committerJulien HENRY <julien.henry@sonarsource.com>2014-05-07 13:03:34 +0200
commit12077a645e769be0d4387f30a1e5f5c1966dc08e (patch)
tree5c3b3c2c5e344a6f26b6c501330614827cac39de /sonar-batch
parenta593b587293f7e7948db5fdfc714ac25d0c0e23f (diff)
downloadsonarqube-12077a645e769be0d4387f30a1e5f5c1966dc08e.tar.gz
sonarqube-12077a645e769be0d4387f30a1e5f5c1966dc08e.zip
SONAR-5189 Reintroduce a memory cache for measure in decorators
Diffstat (limited to 'sonar-batch')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java83
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java38
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java18
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java7
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java4
-rw-r--r--sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java37
6 files changed, 124 insertions, 63 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java
index d33647ee5f3..d31b204e938 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java
@@ -19,26 +19,31 @@
*/
package org.sonar.batch;
-import org.sonar.core.measure.MeasurementFilters;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
-
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.Event;
import org.sonar.api.batch.SonarIndex;
import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.measures.MeasuresFilter;
import org.sonar.api.measures.MeasuresFilters;
import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.MetricFinder;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.rules.Violation;
+import org.sonar.api.utils.SonarException;
import org.sonar.api.violations.ViolationQuery;
+import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.core.measure.MeasurementFilters;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
public class DefaultDecoratorContext implements DecoratorContext {
@@ -50,19 +55,35 @@ public class DefaultDecoratorContext implements DecoratorContext {
private List<DecoratorContext> childrenContexts;
+ private ListMultimap<String, Measure> measuresByMetric = ArrayListMultimap.create();
+ private MeasureCache measureCache;
+ private MetricFinder metricFinder;
+
public DefaultDecoratorContext(Resource resource,
- SonarIndex index,
- List<DecoratorContext> childrenContexts,
- MeasurementFilters measurementFilters) {
+ SonarIndex index,
+ List<DecoratorContext> childrenContexts,
+ MeasurementFilters measurementFilters, MeasureCache measureCache, MetricFinder metricFinder) {
this.index = index;
this.resource = resource;
this.childrenContexts = childrenContexts;
this.measurementFilters = measurementFilters;
+ this.measureCache = measureCache;
+ this.metricFinder = metricFinder;
+ }
+
+ public void init() {
+ Iterable<Measure> unfiltered = measureCache.byResource(resource);
+ for (Measure measure : unfiltered) {
+ measuresByMetric.put(measure.getMetricKey(), measure);
+ }
}
- public DefaultDecoratorContext setReadOnly(boolean b) {
- readOnly = b;
+ public DefaultDecoratorContext end() {
+ readOnly = true;
childrenContexts = null;
+ for (Measure measure : measuresByMetric.values()) {
+ measureCache.put(resource, measure);
+ }
return this;
}
@@ -82,11 +103,18 @@ public class DefaultDecoratorContext implements DecoratorContext {
}
public <M> M getMeasures(MeasuresFilter<M> filter) {
- return index.getMeasures(resource, filter);
+ Collection<Measure> unfiltered;
+ if (filter instanceof MeasuresFilters.MetricFilter) {
+ // optimization
+ unfiltered = measuresByMetric.get(((MeasuresFilters.MetricFilter<M>) filter).filterOnMetricKey());
+ } else {
+ unfiltered = measuresByMetric.values();
+ }
+ return filter.filter(unfiltered);
}
public Measure getMeasure(Metric metric) {
- return index.getMeasure(resource, metric);
+ return getMeasures(MeasuresFilters.metric(metric));
}
public Collection<Measure> getChildrenMeasures(MeasuresFilter filter) {
@@ -114,8 +142,31 @@ public class DefaultDecoratorContext implements DecoratorContext {
public DecoratorContext saveMeasure(Measure measure) {
checkReadOnly(SAVE_MEASURE_METHOD);
- if(measurementFilters.accept(resource, measure)) {
- index.addMeasure(resource, measure);
+ Metric metric = metricFinder.findByKey(measure.getMetricKey());
+ if (metric == null) {
+ throw new SonarException("Unknown metric: " + measure.getMetricKey());
+ }
+ measure.setMetric(metric);
+ if (measurementFilters.accept(resource, measure)) {
+ List<Measure> metricMeasures = measuresByMetric.get(measure.getMetricKey());
+
+ 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.getMetricKey(), measure);
+ }
}
return this;
}
diff --git a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
index ad5516879f1..44db4e53f73 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/index/DefaultIndex.java
@@ -31,8 +31,20 @@ import org.sonar.api.batch.SonarIndex;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.design.Dependency;
-import org.sonar.api.measures.*;
-import org.sonar.api.resources.*;
+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.measures.Metric;
+import org.sonar.api.measures.MetricFinder;
+import org.sonar.api.resources.Directory;
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ProjectLink;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.resources.Scopes;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.Violation;
import org.sonar.api.scan.filesystem.PathResolver;
@@ -41,7 +53,6 @@ import org.sonar.api.violations.ViolationQuery;
import org.sonar.batch.ProjectTree;
import org.sonar.batch.issue.DeprecatedViolations;
import org.sonar.batch.issue.ModuleIssues;
-import org.sonar.batch.qualitygate.QualityGateVerifier;
import org.sonar.batch.scan.measure.MeasureCache;
import org.sonar.core.component.ComponentKeys;
import org.sonar.core.component.ScanGraph;
@@ -49,7 +60,15 @@ import org.sonar.core.component.ScanGraph;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class DefaultIndex extends SonarIndex {
@@ -202,17 +221,6 @@ public class DefaultIndex extends SonarIndex {
return measure;
}
- /**
- * Used by some core features like TendencyDecorator, {@link QualityGateVerifier}, VariationDecorator
- * that need to update some existing measures
- */
- public void updateMeasure(Resource resource, Measure measure) {
- if (!measureCache.contains(resource, measure)) {
- throw new SonarException("Can't update measure on " + resource + ": " + measure);
- }
- measureCache.put(resource, measure);
- }
-
//
//
//
diff --git a/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java
index dc4d0b35049..c6aa4ac8a79 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/phases/DecoratorsExecutor.java
@@ -19,14 +19,13 @@
*/
package org.sonar.batch.phases;
-import org.sonar.core.measure.MeasurementFilters;
-
import com.google.common.collect.Lists;
import org.sonar.api.BatchComponent;
import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.measures.MetricFinder;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.utils.MessageException;
@@ -34,6 +33,8 @@ import org.sonar.api.utils.SonarException;
import org.sonar.batch.DecoratorsSelector;
import org.sonar.batch.DefaultDecoratorContext;
import org.sonar.batch.events.EventBus;
+import org.sonar.batch.scan.measure.MeasureCache;
+import org.sonar.core.measure.MeasurementFilters;
import java.util.Collection;
import java.util.List;
@@ -45,9 +46,13 @@ public class DecoratorsExecutor implements BatchComponent {
private EventBus eventBus;
private Project project;
private MeasurementFilters measurementFilters;
+ private MeasureCache measureCache;
+ private MetricFinder metricFinder;
public DecoratorsExecutor(BatchExtensionDictionnary batchExtDictionnary,
- Project project, SonarIndex index, EventBus eventBus, MeasurementFilters measurementFilters) {
+ Project project, SonarIndex index, EventBus eventBus, MeasurementFilters measurementFilters, MeasureCache measureCache, MetricFinder metricFinder) {
+ this.measureCache = measureCache;
+ this.metricFinder = metricFinder;
this.decoratorsSelector = new DecoratorsSelector(batchExtDictionnary);
this.index = index;
this.eventBus = eventBus;
@@ -58,7 +63,7 @@ public class DecoratorsExecutor implements BatchComponent {
public void execute() {
Collection<Decorator> decorators = decoratorsSelector.select(project);
eventBus.fireEvent(new DecoratorsPhaseEvent(Lists.newArrayList(decorators), true));
- decorateResource(project, decorators, true);
+ ((DefaultDecoratorContext) decorateResource(project, decorators, true)).end();
eventBus.fireEvent(new DecoratorsPhaseEvent(Lists.newArrayList(decorators), false));
}
@@ -67,10 +72,11 @@ public class DecoratorsExecutor implements BatchComponent {
for (Resource child : index.getChildren(resource)) {
boolean isModule = child instanceof Project;
DefaultDecoratorContext childContext = (DefaultDecoratorContext) decorateResource(child, decorators, !isModule);
- childrenContexts.add(childContext.setReadOnly(true));
+ childrenContexts.add(childContext.end());
}
- DefaultDecoratorContext context = new DefaultDecoratorContext(resource, index, childrenContexts, measurementFilters);
+ DefaultDecoratorContext context = new DefaultDecoratorContext(resource, index, childrenContexts, measurementFilters, measureCache, metricFinder);
+ context.init();
if (executeDecorators) {
for (Decorator decorator : decorators) {
executeDecorator(decorator, context, resource);
diff --git a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
index ac18daadf95..872381d6f5f 100644
--- a/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
+++ b/sonar-batch/src/main/java/org/sonar/batch/qualitygate/QualityGateVerifier.java
@@ -38,7 +38,6 @@ import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
-import org.sonar.batch.index.DefaultIndex;
import org.sonar.core.qualitygate.db.QualityGateConditionDto;
import org.sonar.core.timemachine.Periods;
@@ -64,15 +63,13 @@ public class QualityGateVerifier implements Decorator {
private Periods periods;
private I18n i18n;
private Durations durations;
- private final DefaultIndex index;
- public QualityGateVerifier(QualityGate qualityGate, Snapshot snapshot, Periods periods, I18n i18n, Durations durations, DefaultIndex index) {
+ public QualityGateVerifier(QualityGate qualityGate, Snapshot snapshot, Periods periods, I18n i18n, Durations durations) {
this.qualityGate = qualityGate;
this.snapshot = snapshot;
this.periods = periods;
this.i18n = i18n;
this.durations = durations;
- this.index = index;
}
@DependedUpon
@@ -123,7 +120,7 @@ public class QualityGateVerifier implements Decorator {
labels.add(text);
}
- index.updateMeasure(resource, measure);
+ context.saveMeasure(measure);
if (Metric.Level.WARN == level && globalLevel != Metric.Level.ERROR) {
globalLevel = Metric.Level.WARN;
diff --git a/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java b/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java
index 3534236e503..12d35f08b4d 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/phases/DecoratorsExecutorTest.java
@@ -24,12 +24,14 @@ import org.sonar.api.batch.BatchExtensionDictionnary;
import org.sonar.api.batch.Decorator;
import org.sonar.api.batch.DecoratorContext;
import org.sonar.api.batch.SonarIndex;
+import org.sonar.api.measures.MetricFinder;
import org.sonar.api.resources.File;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.DefaultDecoratorContext;
import org.sonar.batch.events.EventBus;
+import org.sonar.batch.scan.measure.MeasureCache;
import org.sonar.core.measure.MeasurementFilters;
import static org.fest.assertions.Assertions.assertThat;
@@ -64,7 +66,7 @@ public class DecoratorsExecutorTest {
doThrow(new SonarException()).when(decorator).decorate(any(Resource.class), any(DecoratorContext.class));
DecoratorsExecutor executor = new DecoratorsExecutor(mock(BatchExtensionDictionnary.class), new Project("key"), mock(SonarIndex.class),
- mock(EventBus.class), mock(MeasurementFilters.class));
+ mock(EventBus.class), mock(MeasurementFilters.class), mock(MeasureCache.class), mock(MetricFinder.class));
try {
executor.executeDecorator(decorator, mock(DefaultDecoratorContext.class), File.create("src/org/foo/Bar.java", "org/foo/Bar.java", null, false));
fail("Exception has not been thrown");
diff --git a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
index 28339d61c9c..9dd6a731f12 100644
--- a/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
+++ b/sonar-batch/src/test/java/org/sonar/batch/qualitygate/QualityGateVerifierTest.java
@@ -39,7 +39,6 @@ import org.sonar.api.resources.Resource;
import org.sonar.api.test.IsMeasure;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
-import org.sonar.batch.index.DefaultIndex;
import org.sonar.core.qualitygate.db.QualityGateConditionDto;
import org.sonar.core.timemachine.Periods;
@@ -70,7 +69,6 @@ public class QualityGateVerifierTest {
Periods periods;
I18n i18n;
Durations durations;
- private DefaultIndex index;
@Before
public void before() {
@@ -91,8 +89,7 @@ public class QualityGateVerifierTest {
snapshot = mock(Snapshot.class);
qualityGate = mock(QualityGate.class);
when(qualityGate.isEnabled()).thenReturn(true);
- index = mock(DefaultIndex.class);
- verifier = new QualityGateVerifier(qualityGate, snapshot, periods, i18n, durations, index);
+ verifier = new QualityGateVerifier(qualityGate, snapshot, periods, i18n, durations);
project = new Project("foo");
}
@@ -133,8 +130,8 @@ public class QualityGateVerifierTest {
verifier.decorate(project, context);
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString())));
verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_GATE_DETAILS, "{\"level\":\"OK\","
+ "\"conditions\":"
@@ -169,8 +166,8 @@ public class QualityGateVerifierTest {
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
}
@@ -187,8 +184,8 @@ public class QualityGateVerifierTest {
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, null)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.WARN)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.ERROR)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.ERROR)));
}
@Test
@@ -258,9 +255,9 @@ public class QualityGateVerifierTest {
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.OK)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureComplexity, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.OK)));
}
@Test
@@ -286,9 +283,9 @@ public class QualityGateVerifierTest {
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.WARN)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureComplexity, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureComplexity, Metric.Level.WARN)));
}
@Test
@@ -302,7 +299,7 @@ public class QualityGateVerifierTest {
verifier.decorate(project, context);
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
}
@Test
@@ -320,7 +317,7 @@ public class QualityGateVerifierTest {
verifier.decorate(project, context);
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.OK, null)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureRatingMetric, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureRatingMetric, Metric.Level.OK)));
}
@Test
@@ -335,7 +332,7 @@ public class QualityGateVerifierTest {
verifier.decorate(project, context);
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
- verify(index).updateMeasure(eq(project), argThat(hasLevel(measureClasses, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
}
@Test(expected = NotImplementedException.class)
@@ -408,7 +405,7 @@ public class QualityGateVerifierTest {
verifier.decorate(project, context);
// First call to saveMeasure is for the update of debt
- verify(index).updateMeasure(eq(project), argThat(matchesMetric(metric, Level.ERROR, "The Debt > 1h")));
+ verify(context).saveMeasure(argThat(matchesMetric(metric, Level.ERROR, "The Debt > 1h")));
verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "The Debt > 1h")));
verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.QUALITY_GATE_DETAILS, "{\"level\":\"ERROR\","
+ "\"conditions\":"