diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2014-05-07 11:37:06 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2014-05-07 13:03:34 +0200 |
commit | 12077a645e769be0d4387f30a1e5f5c1966dc08e (patch) | |
tree | 5c3b3c2c5e344a6f26b6c501330614827cac39de /sonar-batch | |
parent | a593b587293f7e7948db5fdfc714ac25d0c0e23f (diff) | |
download | sonarqube-12077a645e769be0d4387f30a1e5f5c1966dc08e.tar.gz sonarqube-12077a645e769be0d4387f30a1e5f5c1966dc08e.zip |
SONAR-5189 Reintroduce a memory cache for measure in decorators
Diffstat (limited to 'sonar-batch')
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\":" |