]> source.dussan.org Git - sonarqube.git/commitdiff
Filter metrics that can be read from the batch
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 10 Jul 2015 10:33:09 +0000 (12:33 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Wed, 15 Jul 2015 08:44:32 +0000 (10:44 +0200)
Only plugin metrics and some defined core metrics are allowed to be read from the batch

server/sonar-server/src/main/java/org/sonar/server/computation/container/ComputeEngineContainerImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/measure/MeasureRepositoryImpl.java
server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricModule.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/metric/ReportMetricValidator.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/metric/ReportMetricValidatorImpl.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/computation/step/PersistMeasuresStep.java
server/sonar-server/src/test/java/org/sonar/server/computation/measure/MeasureRepositoryImplTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/metric/ReportMetricValidatorImplTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/step/PersistMeasuresStepTest.java
sonar-core/src/main/java/org/sonar/core/metric/SensorMetrics.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/metric/SensorMetricsTest.java [new file with mode: 0644]

index bf03be563776ee8feda2ec35940fc9f24c10afcd..ebd3a564caf6a21cce6af5b3eb50a9ddfa5cfe7a 100644 (file)
@@ -29,10 +29,9 @@ import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
 import org.picocontainer.monitors.NullComponentMonitor;
 import org.sonar.api.utils.log.Loggers;
 import org.sonar.api.utils.log.Profiler;
-import org.sonar.core.platform.Module;
-import org.sonar.server.computation.issue.UpdateConflictResolver;
 import org.sonar.core.issue.tracking.Tracker;
 import org.sonar.core.platform.ComponentContainer;
+import org.sonar.core.platform.Module;
 import org.sonar.server.computation.ComputationService;
 import org.sonar.server.computation.ReportQueue;
 import org.sonar.server.computation.activity.ActivityManager;
@@ -63,10 +62,11 @@ import org.sonar.server.computation.issue.ScmAccountToUserLoader;
 import org.sonar.server.computation.issue.TrackerBaseInputFactory;
 import org.sonar.server.computation.issue.TrackerExecution;
 import org.sonar.server.computation.issue.TrackerRawInputFactory;
+import org.sonar.server.computation.issue.UpdateConflictResolver;
 import org.sonar.server.computation.language.LanguageRepositoryImpl;
 import org.sonar.server.computation.measure.MeasureRepositoryImpl;
 import org.sonar.server.computation.measure.newcoverage.NewCoverageMetricKeysModule;
-import org.sonar.server.computation.metric.MetricRepositoryImpl;
+import org.sonar.server.computation.metric.MetricModule;
 import org.sonar.server.computation.period.PeriodsHolderImpl;
 import org.sonar.server.computation.qualitygate.EvaluationResultTextConverterImpl;
 import org.sonar.server.computation.qualitygate.QualityGateHolderImpl;
@@ -143,6 +143,8 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
     return Arrays.asList(
       ActivityManager.class,
 
+      MetricModule.class,
+
       // holders
       BatchReportDirectoryHolderImpl.class,
       TreeRootHolderImpl.class,
@@ -155,7 +157,6 @@ public class ComputeEngineContainerImpl extends ComponentContainer implements Co
 
       // repositories
       LanguageRepositoryImpl.class,
-      MetricRepositoryImpl.class,
       MeasureRepositoryImpl.class,
       EventRepositoryImpl.class,
       ProjectSettingsRepository.class,
index 4a180c4655bd29f726492e45bcd2b049600c181b..04b5fa46cb2b86a94f8e572501c55658c394a27f 100644 (file)
@@ -37,6 +37,7 @@ import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.debt.Characteristic;
 import org.sonar.server.computation.metric.Metric;
 import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.metric.ReportMetricValidator;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.String.format;
@@ -47,13 +48,16 @@ public class MeasureRepositoryImpl implements MeasureRepository {
   private final BatchReportReader reportReader;
   private final BatchMeasureToMeasure batchMeasureToMeasure;
   private final MetricRepository metricRepository;
+  private final ReportMetricValidator reportMetricValidator;
+
   private final MeasureDtoToMeasure measureDtoToMeasure = new MeasureDtoToMeasure();
   private final Set<Integer> loadedComponents = new HashSet<>();
   private final Map<Integer, Map<MeasureKey, Measure>> measures = new HashMap<>();
 
-  public MeasureRepositoryImpl(DbClient dbClient, BatchReportReader reportReader, MetricRepository metricRepository) {
+  public MeasureRepositoryImpl(DbClient dbClient, BatchReportReader reportReader, MetricRepository metricRepository, ReportMetricValidator reportMetricValidator) {
     this.dbClient = dbClient;
     this.reportReader = reportReader;
+    this.reportMetricValidator = reportMetricValidator;
     this.batchMeasureToMeasure = new BatchMeasureToMeasure();
     this.metricRepository = metricRepository;
   }
@@ -181,8 +185,11 @@ public class MeasureRepositoryImpl implements MeasureRepository {
     }
 
     for (BatchReport.Measure batchMeasure : reportReader.readComponentMeasures(component.getRef())) {
-      Metric metric = metricRepository.getByKey(batchMeasure.getMetricKey());
-      addLocal(component, metric, batchMeasureToMeasure.toMeasure(batchMeasure, metric).get(), OverridePolicy.DO_NOT_OVERRIDE);
+      String metricKey = batchMeasure.getMetricKey();
+      if (reportMetricValidator.validate(metricKey)) {
+        Metric metric = metricRepository.getByKey(metricKey);
+        addLocal(component, metric, batchMeasureToMeasure.toMeasure(batchMeasure, metric).get(), OverridePolicy.DO_NOT_OVERRIDE);
+      }
     }
     loadedComponents.add(component.getRef());
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricModule.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricModule.java
new file mode 100644 (file)
index 0000000..becdb49
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.server.computation.metric;
+
+import org.sonar.core.metric.SensorMetrics;
+import org.sonar.core.platform.Module;
+
+public class MetricModule extends Module {
+  @Override
+  protected void configureModule() {
+    add(
+      SensorMetrics.class,
+      ReportMetricValidatorImpl.class,
+      MetricRepositoryImpl.class);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/ReportMetricValidator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/ReportMetricValidator.java
new file mode 100644 (file)
index 0000000..e5c66fe
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.server.computation.metric;
+
+/**
+ * Validate metric to know if it can be read from the batch
+ */
+public interface ReportMetricValidator {
+
+  boolean validate(String metricKey);
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/ReportMetricValidatorImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/ReportMetricValidatorImpl.java
new file mode 100644 (file)
index 0000000..851dc8a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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.server.computation.metric;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.metric.SensorMetrics;
+
+public class ReportMetricValidatorImpl implements ReportMetricValidator {
+
+  private static final Logger LOG = Loggers.get(ReportMetricValidatorImpl.class);
+
+  private Map<String, org.sonar.api.measures.Metric> metricByKey;
+  private Set<String> alreadyLoggedMetricKeys = new HashSet<>();
+
+  public ReportMetricValidatorImpl(SensorMetrics sensorMetrics) {
+    this.metricByKey = FluentIterable.from(sensorMetrics.getMetrics()).uniqueIndex(MetricToKey.INSTANCE);
+  }
+
+  @Override
+  public boolean validate(String metricKey) {
+    org.sonar.api.measures.Metric metric = metricByKey.get(metricKey);
+    if (metric == null) {
+      if (!alreadyLoggedMetricKeys.contains(metricKey)) {
+        LOG.warn("The metric '{}' is ignored and should not be send in the batch report", metricKey);
+        alreadyLoggedMetricKeys.add(metricKey);
+      }
+      return false;
+    }
+    return true;
+  }
+
+  private enum MetricToKey implements Function<org.sonar.api.measures.Metric, String> {
+    INSTANCE;
+
+    @Nullable
+    @Override
+    public String apply(@Nonnull org.sonar.api.measures.Metric input) {
+      return input.key();
+    }
+  }
+}
index 263517c5349b51d42c211dc53d0b52fe9bd288bc..a6f6c47e552280fdc9b6502329d1adbe70c6e886 100644 (file)
@@ -29,8 +29,9 @@ import java.util.List;
 import java.util.Map;
 import javax.annotation.Nonnull;
 import org.sonar.api.measures.CoreMetrics;
-import org.sonar.db.measure.MeasureDto;
+import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.measure.MeasureDto;
 import org.sonar.server.computation.component.Component;
 import org.sonar.server.computation.component.DbIdsRepository;
 import org.sonar.server.computation.component.DepthTraversalTypeAwareVisitor;
@@ -41,7 +42,6 @@ import org.sonar.server.computation.measure.MeasureRepository;
 import org.sonar.server.computation.measure.MeasureToMeasureDto;
 import org.sonar.server.computation.metric.Metric;
 import org.sonar.server.computation.metric.MetricRepository;
-import org.sonar.db.DbClient;
 
 import static com.google.common.collect.FluentIterable.from;
 import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_ORDER;
@@ -49,12 +49,7 @@ import static org.sonar.server.computation.component.ComponentVisitor.Order.PRE_
 public class PersistMeasuresStep implements ComputationStep {
 
   /**
-   * List of metrics that should not be received from the report, as they should only be fed by the compute engine
-   */
-  private static final List<String> FORBIDDEN_METRIC_KEYS = ImmutableList.of(CoreMetrics.DUPLICATIONS_DATA_KEY);
-
-  /**
-   * List of metrics that should be persisted on file measure (Waiting for SONAR-6688 to be implemented)
+   * List of metrics that should not be persisted on file measure (Waiting for SONAR-6688 to be implemented)
    */
   private static final List<String> NOT_TO_PERSIST_ON_FILE_METRIC_KEYS = ImmutableList.of(
     CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY,
@@ -112,9 +107,6 @@ public class PersistMeasuresStep implements ComputationStep {
     private void persistMeasures(Component component, Multimap<String, Measure> batchReportMeasures, long componentId, long snapshotId) {
       for (Map.Entry<String, Collection<Measure>> measures : batchReportMeasures.asMap().entrySet()) {
         String metricKey = measures.getKey();
-        if (FORBIDDEN_METRIC_KEYS.contains(metricKey)) {
-          throw new IllegalStateException(String.format("Measures on metric '%s' cannot be send in the report", metricKey));
-        }
         if (NOT_TO_PERSIST_ON_FILE_METRIC_KEYS.contains(metricKey) && component.getType() == Component.Type.FILE) {
           continue;
         }
index 114c1305c844f3b3feb4056c675eab015825935d..d5d704afd3a7429b2d2b78bb979b1e815276df3c 100644 (file)
@@ -53,6 +53,7 @@ import org.sonar.server.computation.debt.Characteristic;
 import org.sonar.server.computation.metric.Metric;
 import org.sonar.server.computation.metric.MetricImpl;
 import org.sonar.server.computation.metric.MetricRepository;
+import org.sonar.server.computation.metric.ReportMetricValidator;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.metric.persistence.MetricDao;
 
@@ -90,13 +91,15 @@ public class MeasureRepositoryImplTest {
   private static final RuleDto SOME_RULE = RuleDto.createFor(RuleKey.of("A", "1")).setId(963);
   private static final Characteristic SOME_CHARACTERISTIC = new Characteristic(741, "key", null);
 
+  private ReportMetricValidator reportMetricValidator = mock(ReportMetricValidator.class);
+
   private DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MeasureDao(), new SnapshotDao(), new MetricDao(), new ComponentDao());
   private MetricRepository metricRepository = mock(MetricRepository.class);
-  private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository);
+  private MeasureRepositoryImpl underTest = new MeasureRepositoryImpl(dbClient, reportReader, metricRepository, reportMetricValidator);
 
   private DbClient mockedDbClient = mock(DbClient.class);
   private BatchReportReader mockBatchReportReader = mock(BatchReportReader.class);
-  private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository);
+  private MeasureRepositoryImpl underTestWithMock = new MeasureRepositoryImpl(mockedDbClient, mockBatchReportReader, metricRepository, reportMetricValidator);
 
   @CheckForNull
   private DbSession dbSession;
@@ -217,7 +220,7 @@ public class MeasureRepositoryImplTest {
     Measure.newMeasureBuilder().create("sds"),
     Measure.newMeasureBuilder().create(Measure.Level.OK),
     Measure.newMeasureBuilder().createNoValue()
-  );
+    );
 
   @DataProvider
   public static Object[][] measures() {
@@ -225,7 +228,7 @@ public class MeasureRepositoryImplTest {
       @Nullable
       @Override
       public Object[] apply(Measure input) {
-        return new Measure[]{input};
+        return new Measure[] {input};
       }
     }).toArray(Object[].class);
   }
@@ -352,9 +355,11 @@ public class MeasureRepositoryImplTest {
   public void getRawMeasure_returns_measure_from_batch_if_not_added_through_add_method() {
     String value = "trololo";
 
+    when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
+
     reportReader.putMeasures(FILE_COMPONENT.getRef(), ImmutableList.of(
       BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue(value).build()
-    ));
+      ));
 
     Optional<Measure> res = underTest.getRawMeasure(FILE_COMPONENT, metric1);
 
@@ -366,11 +371,26 @@ public class MeasureRepositoryImplTest {
     assertThat(underTest.getRawMeasure(OTHER_COMPONENT, metric1)).isAbsent();
   }
 
+  @Test
+  public void getRawMeasure_returns_only_validate_measure_from_batch_if_not_added_through_add_method() {
+    when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
+    when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(false);
+
+    reportReader.putMeasures(FILE_COMPONENT.getRef(), ImmutableList.of(
+      BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue("value1").build(),
+      BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue("value2").build()
+      ));
+
+    assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric1)).isPresent();
+    assertThat(underTest.getRawMeasure(FILE_COMPONENT, metric2)).isAbsent();
+  }
+
   @Test
   public void getRawMeasure_retrieves_added_measure_over_batch_measure() {
+    when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
     reportReader.putMeasures(FILE_COMPONENT.getRef(), ImmutableList.of(
       BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue("some value").build()
-    ));
+      ));
 
     Measure addedMeasure = SOME_MEASURE;
     underTest.add(FILE_COMPONENT, metric1, addedMeasure);
@@ -383,9 +403,10 @@ public class MeasureRepositoryImplTest {
 
   @Test
   public void getRawMeasure_retrieves_measure_from_batch_and_caches_it_locally_so_that_it_can_be_updated() {
+    when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
     reportReader.putMeasures(FILE_COMPONENT.getRef(), ImmutableList.of(
       BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue("some value").build()
-    ));
+      ));
 
     Optional<Measure> measure = underTest.getRawMeasure(FILE_COMPONENT, metric1);
 
@@ -444,6 +465,7 @@ public class MeasureRepositoryImplTest {
 
   @Test
   public void getRawMeasure_for_characteristic_returns_measure_for_specified_rule() {
+    when(reportMetricValidator.validate(metric1.getKey())).thenReturn(true);
     Measure measure = Measure.newMeasureBuilder().forCharacteristic(SOME_CHARACTERISTIC.getId()).createNoValue();
 
     underTest.add(FILE_COMPONENT, metric1, measure);
@@ -454,6 +476,8 @@ public class MeasureRepositoryImplTest {
 
   @Test
   public void getRawMeasures_returns_added_measures_over_batch_measures() {
+    when(reportMetricValidator.validate(METRIC_KEY_1)).thenReturn(true);
+    when(reportMetricValidator.validate(METRIC_KEY_2)).thenReturn(true);
     BatchReport.Measure batchMeasure1 = BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_1).setStringValue("some value").build();
     BatchReport.Measure batchMeasure2 = BatchReport.Measure.newBuilder().setMetricKey(METRIC_KEY_2).setStringValue("some value").build();
     reportReader.putMeasures(FILE_COMPONENT.getRef(), ImmutableList.of(batchMeasure1, batchMeasure2));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/metric/ReportMetricValidatorImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/ReportMetricValidatorImplTest.java
new file mode 100644 (file)
index 0000000..5c98e9b
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.server.computation.metric;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.core.metric.SensorMetrics;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.measures.Metric.Builder;
+import static org.sonar.api.measures.Metric.ValueType;
+
+public class ReportMetricValidatorImplTest {
+
+  @Rule
+  public LogTester logTester = new LogTester();
+
+  static final String METRIC_KEY = "metric_key";
+
+  SensorMetrics sensorMetrics = mock(SensorMetrics.class);
+
+  @Test
+  public void validate_metric() throws Exception {
+    when(sensorMetrics.getMetrics()).thenReturn(ImmutableSet.<Metric>of(new Builder(METRIC_KEY, "name", ValueType.INT).create()));
+    ReportMetricValidator validator = new ReportMetricValidatorImpl(sensorMetrics);
+
+    assertThat(validator.validate(METRIC_KEY)).isTrue();
+    assertThat(logTester.logs()).isEmpty();
+  }
+
+  @Test
+  public void not_validate_metric() throws Exception {
+    when(sensorMetrics.getMetrics()).thenReturn(Collections.<Metric>emptySet());
+    ReportMetricValidator validator = new ReportMetricValidatorImpl(sensorMetrics);
+
+    assertThat(validator.validate(METRIC_KEY)).isFalse();
+    assertThat(logTester.logs()).containsOnly("The metric 'metric_key' is ignored and should not be send in the batch report");
+  }
+
+  @Test
+  public void not_generate_new_log_when_validating_twice_the_same_metric() throws Exception {
+    when(sensorMetrics.getMetrics()).thenReturn(Collections.<Metric>emptySet());
+    ReportMetricValidator validator = new ReportMetricValidatorImpl(sensorMetrics);
+
+    assertThat(validator.validate(METRIC_KEY)).isFalse();
+    assertThat(logTester.logs()).hasSize(1);
+    assertThat(validator.validate(METRIC_KEY)).isFalse();
+    assertThat(logTester.logs()).hasSize(1);
+  }
+}
index b15f3172f695d896d8588a4355e486167aa36fcb..665ea3c43c743a537e6b6f0a05926592ec128c08 100644 (file)
@@ -49,8 +49,6 @@ import org.sonar.server.rule.db.RuleDao;
 import org.sonar.test.DbTests;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA;
-import static org.sonar.api.measures.CoreMetrics.DUPLICATIONS_DATA_KEY;
 import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION;
 import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION_KEY;
 import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION;
@@ -202,20 +200,6 @@ public class PersistMeasuresStepTest extends BaseStepTest {
     assertThat(retrieveDtos()).isEmpty();
   }
 
-  @Test(expected = IllegalStateException.class)
-  public void fail_with_ISE_when_trying_to_insert_forbidden_measures() {
-    metricRepository.add(1, DUPLICATIONS_DATA);
-
-    reportReader.putMeasures(FILE_REF, Arrays.asList(
-      BatchReport.Measure.newBuilder()
-        .setValueType(MeasureValueType.STRING)
-        .setStringValue("{duplications}")
-        .setMetricKey(DUPLICATIONS_DATA_KEY)
-        .build()));
-
-    sut.execute();
-  }
-
   @Test
   public void do_not_insert_file_complexity_distribution_metric_on_files() {
     metricRepository.add(1, FILE_COMPLEXITY_DISTRIBUTION);
diff --git a/sonar-core/src/main/java/org/sonar/core/metric/SensorMetrics.java b/sonar-core/src/main/java/org/sonar/core/metric/SensorMetrics.java
new file mode 100644 (file)
index 0000000..52f17de
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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.core.metric;
+
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.Metrics;
+
+import static org.sonar.api.measures.CoreMetrics.ACCESSORS;
+import static org.sonar.api.measures.CoreMetrics.CLASSES;
+import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES;
+import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_DATA;
+import static org.sonar.api.measures.CoreMetrics.COMPLEXITY;
+import static org.sonar.api.measures.CoreMetrics.COMPLEXITY_IN_CLASSES;
+import static org.sonar.api.measures.CoreMetrics.COMPLEXITY_IN_FUNCTIONS;
+import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA;
+import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.DIRECTORIES;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_BLOCKS;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_FILES;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES;
+import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES_DENSITY;
+import static org.sonar.api.measures.CoreMetrics.FILES;
+import static org.sonar.api.measures.CoreMetrics.FILE_COMPLEXITY_DISTRIBUTION;
+import static org.sonar.api.measures.CoreMetrics.FUNCTIONS;
+import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION;
+import static org.sonar.api.measures.CoreMetrics.GENERATED_LINES;
+import static org.sonar.api.measures.CoreMetrics.GENERATED_NCLOC;
+import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.IT_COVERAGE_LINE_HITS_DATA;
+import static org.sonar.api.measures.CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.IT_LINES_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_CONDITIONS;
+import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_LINES;
+import static org.sonar.api.measures.CoreMetrics.LINES;
+import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.NCLOC;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_DATA;
+import static org.sonar.api.measures.CoreMetrics.NCLOC_LANGUAGE_DISTRIBUTION;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_LINES_TO_COVER;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_CONDITIONS;
+import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_LINES;
+import static org.sonar.api.measures.CoreMetrics.PUBLIC_API;
+import static org.sonar.api.measures.CoreMetrics.PUBLIC_UNDOCUMENTED_API;
+import static org.sonar.api.measures.CoreMetrics.QUALITY_PROFILES;
+import static org.sonar.api.measures.CoreMetrics.SKIPPED_TESTS;
+import static org.sonar.api.measures.CoreMetrics.STATEMENTS;
+import static org.sonar.api.measures.CoreMetrics.TESTS;
+import static org.sonar.api.measures.CoreMetrics.TEST_ERRORS;
+import static org.sonar.api.measures.CoreMetrics.TEST_EXECUTION_TIME;
+import static org.sonar.api.measures.CoreMetrics.TEST_FAILURES;
+import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS;
+import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES;
+
+/**
+ * This class is used to know the list of metrics that can be send by sensor.
+ * <p/>
+ * The batch should not send other metrics, and the Compute Engine should not allow other metrics from the batch report.
+ */
+public class SensorMetrics {
+
+  private static final Set<Metric> ALLOWED_CORE_METRICS = ImmutableSet.<Metric>of(
+    LINES,
+    GENERATED_LINES,
+    NCLOC,
+    NCLOC_DATA,
+    GENERATED_NCLOC,
+    NCLOC_LANGUAGE_DISTRIBUTION,
+
+    COMMENT_LINES,
+    COMMENT_LINES_DATA,
+
+    PUBLIC_API,
+    PUBLIC_UNDOCUMENTED_API,
+
+    FILES,
+    DIRECTORIES,
+    CLASSES,
+    FUNCTIONS,
+    STATEMENTS,
+    ACCESSORS,
+
+    DUPLICATED_LINES,
+    DUPLICATED_BLOCKS,
+    DUPLICATED_FILES,
+    DUPLICATED_LINES_DENSITY,
+
+    COMPLEXITY,
+    COMPLEXITY_IN_CLASSES,
+    COMPLEXITY_IN_FUNCTIONS,
+    FILE_COMPLEXITY_DISTRIBUTION,
+    FUNCTION_COMPLEXITY_DISTRIBUTION,
+
+    TESTS,
+    SKIPPED_TESTS,
+    TEST_ERRORS,
+    TEST_FAILURES,
+    TEST_EXECUTION_TIME,
+    TEST_SUCCESS_DENSITY,
+
+    LINES_TO_COVER,
+    UNCOVERED_LINES,
+    COVERAGE_LINE_HITS_DATA,
+    CONDITIONS_TO_COVER,
+    UNCOVERED_CONDITIONS,
+    COVERED_CONDITIONS_BY_LINE,
+    CONDITIONS_BY_LINE,
+
+    IT_LINES_TO_COVER,
+    IT_UNCOVERED_LINES,
+    IT_COVERAGE_LINE_HITS_DATA,
+    IT_CONDITIONS_TO_COVER,
+    IT_UNCOVERED_CONDITIONS,
+    IT_COVERED_CONDITIONS_BY_LINE,
+    IT_CONDITIONS_BY_LINE,
+
+    OVERALL_LINES_TO_COVER,
+    OVERALL_UNCOVERED_LINES,
+    OVERALL_COVERAGE_LINE_HITS_DATA,
+    OVERALL_CONDITIONS_TO_COVER,
+    OVERALL_UNCOVERED_CONDITIONS,
+    OVERALL_COVERED_CONDITIONS_BY_LINE,
+    OVERALL_CONDITIONS_BY_LINE,
+
+    QUALITY_PROFILES
+    );
+
+  private final Set<Metric> metrics;
+
+  public SensorMetrics(Metrics[] metricsRepositories) {
+    this.metrics = ImmutableSet.copyOf(Iterables.concat(getPluginMetrics(metricsRepositories), ALLOWED_CORE_METRICS));
+  }
+
+  public Set<Metric> getMetrics() {
+    return metrics;
+  }
+
+  private static Iterable<Metric> getPluginMetrics(Metrics[] metricsRepositories) {
+    return FluentIterable.from(Arrays.asList(metricsRepositories))
+      .transformAndConcat(FlattenMetrics.INSTANCE);
+  }
+
+  private enum FlattenMetrics implements Function<Metrics, List<Metric>> {
+    INSTANCE;
+
+    @Nullable
+    @Override
+    public List<Metric> apply(Metrics input) {
+      return input.getMetrics();
+    }
+  }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/metric/SensorMetricsTest.java b/sonar-core/src/test/java/org/sonar/core/metric/SensorMetricsTest.java
new file mode 100644 (file)
index 0000000..332e5ef
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.core.metric;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import org.junit.Test;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.Metrics;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class SensorMetricsTest {
+
+  static final SensorMetrics SENSOR_METRICS_WITHOUT_METRIC_PLUGIN = new SensorMetrics(new Metrics[]{});
+  static final SensorMetrics SENSOR_METRICS_WITH_PLUGIN = new SensorMetrics(new Metrics[]{new FakeMetrics()});
+
+  @Test
+  public void check_number_of_allowed_core_metrics() throws Exception {
+    assertThat(SENSOR_METRICS_WITHOUT_METRIC_PLUGIN.getMetrics()).hasSize(53);
+  }
+
+  @Test
+  public void check_metrics_from_plugin() throws Exception {
+    List<Metric> metrics = newArrayList(SENSOR_METRICS_WITH_PLUGIN.getMetrics());
+    Iterables.removeAll(metrics, SENSOR_METRICS_WITHOUT_METRIC_PLUGIN.getMetrics());
+    assertThat(metrics).hasSize(2);
+  }
+
+  private static class FakeMetrics implements Metrics {
+
+    @Override
+    public List<Metric> getMetrics() {
+      return ImmutableList.<Metric>of(
+        new Metric.Builder("key1", "name1", Metric.ValueType.INT).create(),
+        new Metric.Builder("key2", "name2", Metric.ValueType.FLOAT).create()
+      );
+    }
+  }
+}