diff options
Diffstat (limited to 'sonar-scanner-engine/src/main')
16 files changed, 199 insertions, 1393 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java index 7ef71bbb221..eecbbd15e84 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContext.java @@ -32,15 +32,12 @@ import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.utils.KeyValueFormat; -import org.sonar.api.utils.KeyValueFormat.Converter; -import org.sonar.scanner.scan.measure.MeasureCache; import static java.util.stream.Collectors.toMap; public class DefaultFileLinesContext implements FileLinesContext { private final InputFile inputFile; private final MetricFinder metricFinder; - private final MeasureCache measureCache; /** * metric key -> line -> value @@ -48,11 +45,10 @@ public class DefaultFileLinesContext implements FileLinesContext { private final Map<String, Map<Integer, Object>> map = new HashMap<>(); private final SensorStorage sensorStorage; - public DefaultFileLinesContext(SensorStorage sensorStorage, InputFile inputFile, MetricFinder metricFinder, MeasureCache measureCache) { + public DefaultFileLinesContext(SensorStorage sensorStorage, InputFile inputFile, MetricFinder metricFinder) { this.sensorStorage = sensorStorage; this.inputFile = inputFile; this.metricFinder = metricFinder; - this.measureCache = measureCache; } @Override @@ -68,13 +64,6 @@ public class DefaultFileLinesContext implements FileLinesContext { Preconditions.checkArgument(line <= inputFile.lines(), "Line %s is out of range for file %s. File has %s lines.", line, inputFile, inputFile.lines()); } - public Integer getIntValue(String metricKey, int line) { - Preconditions.checkNotNull(metricKey); - checkLineRange(line); - Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newIntegerConverter())); - return (Integer) lines.get(line); - } - @Override public void setStringValue(String metricKey, int line, String value) { Preconditions.checkNotNull(metricKey); @@ -84,13 +73,6 @@ public class DefaultFileLinesContext implements FileLinesContext { setValue(metricKey, line, value); } - public String getStringValue(String metricKey, int line) { - Preconditions.checkNotNull(metricKey); - checkLineRange(line); - Map<Integer, Object> lines = map.computeIfAbsent(metricKey, k -> loadData(k, KeyValueFormat.newStringConverter())); - return (String) lines.get(line); - } - private void setValue(String metricKey, int line, Object value) { map.computeIfAbsent(metricKey, k -> new HashMap<>()) .put(line, value); @@ -123,20 +105,9 @@ public class DefaultFileLinesContext implements FileLinesContext { return lines; } - private Map<Integer, Object> loadData(String metricKey, Converter<? extends Object> converter) { - DefaultMeasure<?> measure = measureCache.byMetric(inputFile.key(), metricKey); - String data = measure != null ? (String) measure.value() : null; - if (data != null) { - return ImmutableMap.copyOf(KeyValueFormat.parse(data, KeyValueFormat.newIntegerConverter(), converter)); - } - // no such measure - return ImmutableMap.of(); - } - /** * Checks that measure was not saved. * - * @see #loadData(String, Converter) * @see #save() */ private static boolean shouldSave(Map<Integer, Object> lines) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContextFactory.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContextFactory.java index 8ed50b88710..209f3544810 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContextFactory.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/DefaultFileLinesContextFactory.java @@ -25,24 +25,21 @@ import org.sonar.api.batch.measure.MetricFinder; import org.sonar.api.batch.sensor.internal.SensorStorage; import org.sonar.api.measures.FileLinesContext; import org.sonar.api.measures.FileLinesContextFactory; -import org.sonar.scanner.scan.measure.MeasureCache; @Immutable public class DefaultFileLinesContextFactory implements FileLinesContextFactory { private final SensorStorage sensorStorage; private final MetricFinder metricFinder; - private final MeasureCache measureCache; - public DefaultFileLinesContextFactory(SensorStorage sensorStorage, MetricFinder metricFinder, MeasureCache measureCache) { + public DefaultFileLinesContextFactory(SensorStorage sensorStorage, MetricFinder metricFinder) { this.sensorStorage = sensorStorage; this.metricFinder = metricFinder; - this.measureCache = measureCache; } @Override public FileLinesContext createFor(InputFile inputFile) { - return new DefaultFileLinesContext(sensorStorage, inputFile, metricFinder, measureCache); + return new DefaultFileLinesContext(sensorStorage, inputFile, metricFinder); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java index 4857692b80c..302f263a92f 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/bootstrap/GlobalContainer.java @@ -52,7 +52,6 @@ import org.sonar.scanner.repository.MetricsRepositoryProvider; import org.sonar.scanner.repository.settings.DefaultGlobalSettingsLoader; import org.sonar.scanner.repository.settings.GlobalSettingsLoader; import org.sonar.scanner.scan.ProjectScanContainer; -import org.sonar.scanner.storage.StoragesManager; public class GlobalContainer extends ComponentContainer { private static final Logger LOG = Loggers.get(GlobalContainer.class); @@ -90,7 +89,6 @@ public class GlobalContainer extends ComponentContainer { new SonarQubeVersion(apiVersion), SonarRuntimeImpl.forSonarQube(apiVersion, SonarQubeSide.SCANNER), - StoragesManager.class, new GlobalServerSettingsProvider(), new GlobalConfigurationProvider(), new ScannerWsClientProvider(), diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java deleted file mode 100644 index d2bb71b1cd3..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/CoveragePublisher.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.scanner.report; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import org.apache.commons.lang.StringUtils; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage; -import org.sonar.scanner.protocol.output.ScannerReport.LineCoverage.Builder; -import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.scan.filesystem.InputComponentStore; -import org.sonar.scanner.scan.measure.MeasureCache; - -public class CoveragePublisher implements ReportPublisherStep { - - private final InputComponentStore componentStore; - private final MeasureCache measureCache; - - public CoveragePublisher(InputComponentStore componentStore, MeasureCache measureCache) { - this.componentStore = componentStore; - this.measureCache = measureCache; - } - - @Override - public void publish(ScannerReportWriter writer) { - for (final DefaultInputFile inputFile : componentStore.allFilesToPublish()) { - Map<Integer, LineCoverage.Builder> coveragePerLine = new LinkedHashMap<>(); - - int lineCount = inputFile.lines(); - applyLineMeasure(inputFile.key(), lineCount, CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY, coveragePerLine, - (value, builder) -> builder.setHits(Integer.parseInt(value) > 0)); - applyLineMeasure(inputFile.key(), lineCount, CoreMetrics.CONDITIONS_BY_LINE_KEY, coveragePerLine, - (value, builder) -> builder.setConditions(Integer.parseInt(value))); - applyLineMeasure(inputFile.key(), lineCount, CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY, coveragePerLine, - (value, builder) -> builder.setCoveredConditions(Integer.parseInt(value))); - - writer.writeComponentCoverage(inputFile.scannerId(), coveragePerLine.values().stream().map(BuildCoverage.INSTANCE).collect(Collectors.toList())); - } - } - - void applyLineMeasure(String inputFileKey, int lineCount, String metricKey, Map<Integer, LineCoverage.Builder> coveragePerLine, MeasureOperation op) { - DefaultMeasure<?> measure = measureCache.byMetric(inputFileKey, metricKey); - if (measure != null) { - Map<Integer, String> lineMeasures = KeyValueFormat.parseIntString((String) measure.value()); - for (Map.Entry<Integer, String> lineMeasure : lineMeasures.entrySet()) { - int lineIdx = lineMeasure.getKey(); - if (lineIdx <= lineCount) { - String value = lineMeasure.getValue(); - if (StringUtils.isNotEmpty(value)) { - LineCoverage.Builder coverageBuilder = coveragePerLine.get(lineIdx); - if (coverageBuilder == null) { - coverageBuilder = LineCoverage.newBuilder(); - coverageBuilder.setLine(lineIdx); - coveragePerLine.put(lineIdx, coverageBuilder); - } - op.apply(value, coverageBuilder); - } - } - } - } - } - - interface MeasureOperation { - void apply(String value, LineCoverage.Builder builder); - } - - private enum BuildCoverage implements Function<Builder, LineCoverage> { - INSTANCE; - - @Override - public LineCoverage apply(@Nonnull Builder input) { - return input.build(); - } - } - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java deleted file mode 100644 index 75692161a69..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MeasuresPublisher.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.scanner.report; - -import com.google.common.collect.Iterables; -import java.io.Serializable; -import java.util.Collections; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import org.sonar.api.batch.fs.InputComponent; -import org.sonar.api.batch.fs.InputFile; -import org.sonar.api.batch.fs.InputFile.Type; -import org.sonar.api.batch.fs.internal.DefaultInputComponent; -import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.measure.Metric; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.test.MutableTestPlan; -import org.sonar.api.test.TestCase.Status; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.scanner.deprecated.test.TestPlanBuilder; -import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.scanner.protocol.output.ScannerReport.Measure.BoolValue; -import org.sonar.scanner.protocol.output.ScannerReport.Measure.DoubleValue; -import org.sonar.scanner.protocol.output.ScannerReport.Measure.IntValue; -import org.sonar.scanner.protocol.output.ScannerReport.Measure.LongValue; -import org.sonar.scanner.protocol.output.ScannerReport.Measure.StringValue; -import org.sonar.scanner.protocol.output.ScannerReportWriter; -import org.sonar.scanner.scan.filesystem.InputComponentStore; -import org.sonar.scanner.scan.measure.MeasureCache; - -import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER; -import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER; -import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.SKIPPED_TESTS; -import static org.sonar.api.measures.CoreMetrics.SKIPPED_TESTS_KEY; -import static org.sonar.api.measures.CoreMetrics.TESTS; -import static org.sonar.api.measures.CoreMetrics.TESTS_KEY; -import static org.sonar.api.measures.CoreMetrics.TEST_ERRORS; -import static org.sonar.api.measures.CoreMetrics.TEST_ERRORS_KEY; -import static org.sonar.api.measures.CoreMetrics.TEST_EXECUTION_TIME; -import static org.sonar.api.measures.CoreMetrics.TEST_EXECUTION_TIME_KEY; -import static org.sonar.api.measures.CoreMetrics.TEST_FAILURES; -import static org.sonar.api.measures.CoreMetrics.TEST_FAILURES_KEY; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS_KEY; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES_KEY; - -public class MeasuresPublisher implements ReportPublisherStep { - - private final InputComponentStore componentStore; - private final MeasureCache measureCache; - private final TestPlanBuilder testPlanBuilder; - - public MeasuresPublisher(InputComponentStore componentStore, MeasureCache measureCache, TestPlanBuilder testPlanBuilder) { - this.componentStore = componentStore; - this.measureCache = measureCache; - this.testPlanBuilder = testPlanBuilder; - } - - @Override - public void publish(ScannerReportWriter writer) { - final ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); - - for (final InputComponent c : componentStore.all()) { - DefaultInputComponent component = (DefaultInputComponent) c; - if (component.isFile()) { - DefaultInputFile file = (DefaultInputFile) component; - // Recompute all coverage measures from line data to take into account the possible merge of several reports - updateCoverageFromLineData(file); - // Recompute test execution measures from MutableTestPlan to take into account the possible merge of several reports - updateTestExecutionFromTestPlan(file); - } - - Iterable<DefaultMeasure<?>> scannerMeasures = measureCache.byComponentKey(component.key()); - if (scannerMeasures.iterator().hasNext()) { - writer.writeComponentMeasures(component.scannerId(), StreamSupport.stream(scannerMeasures.spliterator(), false) - .map(input -> { - if (input.value() == null) { - throw new IllegalArgumentException( - String.format("Measure on metric '%s' and component '%s' has no value, but it's not allowed", input.metric().key(), component.key())); - } - builder.clear(); - builder.setMetricKey(input.metric().key()); - setValueAccordingToType(builder, input); - return builder.build(); - }).collect(Collectors.toList())); - } - } - } - - private static void setValueAccordingToType(ScannerReport.Measure.Builder builder, DefaultMeasure<?> measure) { - Serializable value = measure.value(); - Metric<?> metric = measure.metric(); - if (Boolean.class.equals(metric.valueType())) { - builder.setBooleanValue(BoolValue.newBuilder().setValue(((Boolean) value).booleanValue())); - } else if (Integer.class.equals(metric.valueType())) { - builder.setIntValue(IntValue.newBuilder().setValue(((Number) value).intValue())); - } else if (Double.class.equals(metric.valueType())) { - builder.setDoubleValue(DoubleValue.newBuilder().setValue(((Number) value).doubleValue())); - } else if (String.class.equals(metric.valueType())) { - builder.setStringValue(StringValue.newBuilder().setValue((String) value)); - } else if (Long.class.equals(metric.valueType())) { - builder.setLongValue(LongValue.newBuilder().setValue(((Number) value).longValue())); - } else { - throw new UnsupportedOperationException("Unsupported type :" + metric.valueType()); - } - } - - private void updateTestExecutionFromTestPlan(final InputFile inputFile) { - final MutableTestPlan testPlan = testPlanBuilder.getTestPlanByFile(inputFile); - if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { - return; - } - long nonSkippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() != Status.SKIPPED).count(); - measureCache.put(inputFile.key(), TESTS_KEY, new DefaultMeasure<Integer>().forMetric(TESTS).withValue((int) nonSkippedTests)); - long executionTime = StreamSupport.stream(testPlan.testCases().spliterator(), false).mapToLong(t -> t.durationInMs() != null ? t.durationInMs().longValue() : 0L).sum(); - measureCache.put(inputFile.key(), TEST_EXECUTION_TIME_KEY, new DefaultMeasure<Long>().forMetric(TEST_EXECUTION_TIME).withValue(executionTime)); - long errorTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.ERROR).count(); - measureCache.put(inputFile.key(), TEST_ERRORS_KEY, new DefaultMeasure<Integer>().forMetric(TEST_ERRORS).withValue((int) errorTests)); - long skippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.SKIPPED).count(); - measureCache.put(inputFile.key(), SKIPPED_TESTS_KEY, new DefaultMeasure<Integer>().forMetric(SKIPPED_TESTS).withValue((int) skippedTests)); - long failedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.FAILURE).count(); - measureCache.put(inputFile.key(), TEST_FAILURES_KEY, new DefaultMeasure<Integer>().forMetric(TEST_FAILURES).withValue((int) failedTests)); - } - - private void updateCoverageFromLineData(final InputFile inputFile) { - if (inputFile.type() != Type.MAIN) { - return; - } - DefaultMeasure<String> lineHitsMeasure = (DefaultMeasure<String>) measureCache.byMetric(inputFile.key(), CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY); - if (lineHitsMeasure != null) { - Map<Integer, Integer> lineHits = KeyValueFormat.parseIntInt(lineHitsMeasure.value()); - measureCache.put(inputFile.key(), LINES_TO_COVER_KEY, new DefaultMeasure<Integer>().forMetric(LINES_TO_COVER).withValue(lineHits.keySet().size())); - measureCache.put(inputFile.key(), UNCOVERED_LINES_KEY, - new DefaultMeasure<Integer>().forMetric(UNCOVERED_LINES).withValue((int) lineHits.values() - .stream() - .filter(hit -> hit == 0) - .count())); - } - DefaultMeasure<String> conditionsMeasure = (DefaultMeasure<String>) measureCache.byMetric(inputFile.key(), CoreMetrics.CONDITIONS_BY_LINE_KEY); - DefaultMeasure<String> coveredConditionsMeasure = (DefaultMeasure<String>) measureCache.byMetric(inputFile.key(), CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY); - if (conditionsMeasure != null) { - Map<Integer, Integer> conditions = KeyValueFormat.parseIntInt(conditionsMeasure.value()); - Map<Integer, Integer> coveredConditions = coveredConditionsMeasure != null ? KeyValueFormat.parseIntInt(coveredConditionsMeasure.value()) : Collections.emptyMap(); - measureCache.put(inputFile.key(), CONDITIONS_TO_COVER_KEY, new DefaultMeasure<Integer>().forMetric(CONDITIONS_TO_COVER).withValue(conditions - .values() - .stream() - .mapToInt(Integer::intValue) - .sum())); - measureCache.put(inputFile.key(), UNCOVERED_CONDITIONS_KEY, - new DefaultMeasure<Integer>().forMetric(UNCOVERED_CONDITIONS) - .withValue((int) conditions.keySet() - .stream() - .mapToInt(line -> conditions.get(line) - coveredConditions.get(line)) - .sum())); - } - } - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java index 2843699a626..5ed8769d2f9 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java @@ -45,6 +45,7 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.bootstrap.ScannerWsClient; +import org.sonar.scanner.protocol.output.ScannerReportReader; import org.sonar.scanner.protocol.output.ScannerReportWriter; import org.sonar.scanner.scan.ScanProperties; import org.sonar.scanner.scan.branch.BranchConfiguration; @@ -83,6 +84,7 @@ public class ReportPublisher implements Startable { private Path reportDir; private ScannerReportWriter writer; + private ScannerReportReader reader; public ReportPublisher(ScanProperties properties, ScannerWsClient wsClient, Server server, AnalysisContextReportPublisher contextPublisher, InputModuleHierarchy moduleHierarchy, GlobalAnalysisMode analysisMode, TempFolder temp, ReportPublisherStep[] publishers, BranchConfiguration branchConfiguration) { @@ -101,6 +103,7 @@ public class ReportPublisher implements Startable { public void start() { reportDir = moduleHierarchy.root().getWorkDir().resolve("scanner-report"); writer = new ScannerReportWriter(reportDir.toFile()); + reader = new ScannerReportReader(reportDir.toFile()); contextPublisher.init(writer); if (!analysisMode.isMediumTest()) { @@ -126,6 +129,10 @@ public class ReportPublisher implements Startable { return writer; } + public ScannerReportReader getReader() { + return reader; + } + public void execute() { String taskId = null; File report = generateReportFile(); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionPublisher.java new file mode 100644 index 00000000000..9c19a887d68 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionPublisher.java @@ -0,0 +1,90 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program 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. + * + * This program 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.scanner.report; + +import com.google.common.collect.Iterables; +import java.util.Objects; +import java.util.stream.StreamSupport; +import org.sonar.api.batch.fs.InputComponent; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; +import org.sonar.api.test.MutableTestPlan; +import org.sonar.api.test.TestCase; +import org.sonar.api.test.TestCase.Status; +import org.sonar.scanner.deprecated.test.TestPlanBuilder; +import org.sonar.scanner.protocol.output.ScannerReport; +import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.scan.filesystem.InputComponentStore; + +import static org.sonar.api.measures.CoreMetrics.SKIPPED_TESTS; +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.scanner.sensor.DefaultSensorStorage.toReportMeasure; + +public class TestExecutionPublisher implements ReportPublisherStep { + + private final InputComponentStore componentStore; + private final TestPlanBuilder testPlanBuilder; + + public TestExecutionPublisher(InputComponentStore componentStore, TestPlanBuilder testPlanBuilder) { + this.componentStore = componentStore; + this.testPlanBuilder = testPlanBuilder; + } + + @Override + public void publish(ScannerReportWriter writer) { + final ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); + + for (final InputComponent c : componentStore.all()) { + DefaultInputComponent component = (DefaultInputComponent) c; + if (component.isFile()) { + DefaultInputFile file = (DefaultInputFile) component; + // Recompute test execution measures from MutableTestPlan to take into account the possible merge of several reports + updateTestExecutionFromTestPlan(file, writer); + } + } + } + + private void updateTestExecutionFromTestPlan(final InputFile inputFile, ScannerReportWriter writer) { + final MutableTestPlan testPlan = testPlanBuilder.getTestPlanByFile(inputFile); + if (testPlan == null || Iterables.isEmpty(testPlan.testCases())) { + return; + } + long nonSkippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() != Status.SKIPPED).count(); + appendMeasure(inputFile, writer, new DefaultMeasure<Integer>().forMetric(TESTS).withValue((int) nonSkippedTests)); + long executionTime = StreamSupport.stream(testPlan.testCases().spliterator(), false).map(TestCase::durationInMs).filter(Objects::nonNull).mapToLong(Long::longValue).sum(); + appendMeasure(inputFile, writer, new DefaultMeasure<Long>().forMetric(TEST_EXECUTION_TIME).withValue(executionTime)); + long errorTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.ERROR).count(); + appendMeasure(inputFile, writer, new DefaultMeasure<Integer>().forMetric(TEST_ERRORS).withValue((int) errorTests)); + long skippedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.SKIPPED).count(); + appendMeasure(inputFile, writer, new DefaultMeasure<Integer>().forMetric(SKIPPED_TESTS).withValue((int) skippedTests)); + long failedTests = StreamSupport.stream(testPlan.testCases().spliterator(), false).filter(t -> t.status() == Status.FAILURE).count(); + appendMeasure(inputFile, writer, new DefaultMeasure<Integer>().forMetric(TEST_FAILURES).withValue((int) failedTests)); + } + + private void appendMeasure(InputFile inputFile, ScannerReportWriter writer, DefaultMeasure measure) { + writer.appendComponentMeasure(((DefaultInputComponent) inputFile).scannerId(), toReportMeasure(measure)); + } + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index b18e5db8fae..dc242a2b4bd 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -69,11 +69,10 @@ import org.sonar.scanner.report.AnalysisWarningsPublisher; import org.sonar.scanner.report.ChangedLinesPublisher; import org.sonar.scanner.report.ComponentsPublisher; import org.sonar.scanner.report.ContextPropertiesPublisher; -import org.sonar.scanner.report.CoveragePublisher; -import org.sonar.scanner.report.MeasuresPublisher; import org.sonar.scanner.report.MetadataPublisher; import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.report.SourcePublisher; +import org.sonar.scanner.report.TestExecutionPublisher; import org.sonar.scanner.repository.ContextPropertiesCache; import org.sonar.scanner.repository.DefaultProjectRepositoriesLoader; import org.sonar.scanner.repository.DefaultQualityProfileLoader; @@ -107,7 +106,6 @@ import org.sonar.scanner.scan.filesystem.ProjectFileIndexer; import org.sonar.scanner.scan.filesystem.ScannerComponentIdGenerator; import org.sonar.scanner.scan.filesystem.StatusDetection; import org.sonar.scanner.scan.measure.DefaultMetricFinder; -import org.sonar.scanner.scan.measure.MeasureCache; import org.sonar.scanner.scm.ScmChangedFilesProvider; import org.sonar.scanner.scm.ScmConfiguration; import org.sonar.scanner.scm.ScmPublisher; @@ -116,7 +114,6 @@ import org.sonar.scanner.sensor.ProjectSensorContext; import org.sonar.scanner.sensor.ProjectSensorExtensionDictionnary; import org.sonar.scanner.sensor.ProjectSensorOptimizer; import org.sonar.scanner.sensor.ProjectSensorsExecutor; -import org.sonar.scanner.storage.Storages; import static org.sonar.api.batch.InstantiationStrategy.PER_BATCH; import static org.sonar.core.extension.CoreExtensionsInstaller.noExtensionFilter; @@ -155,7 +152,6 @@ public class ProjectScanContainer extends ComponentContainer { ProjectReactorValidator.class, MetricProvider.class, ProjectInfo.class, - Storages.class, new RulesProvider(), new BranchConfigurationProvider(), new ProjectBranchesProvider(), @@ -205,9 +201,6 @@ public class ProjectScanContainer extends ComponentContainer { Languages.class, DefaultLanguagesRepository.class, - // Measures - MeasureCache.class, - // issue exclusions IssueInclusionPatternInitializer.class, IssueExclusionPatternInitializer.class, @@ -237,8 +230,7 @@ public class ProjectScanContainer extends ComponentContainer { ActiveRulesPublisher.class, AnalysisWarningsPublisher.class, ComponentsPublisher.class, - MeasuresPublisher.class, - CoveragePublisher.class, + TestExecutionPublisher.class, SourcePublisher.class, ChangedLinesPublisher.class, diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureCache.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureCache.java deleted file mode 100644 index 68b30bf84f9..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureCache.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.scanner.scan.measure; - -import com.google.common.base.Preconditions; -import javax.annotation.CheckForNull; -import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.scanner.storage.Storage; -import org.sonar.scanner.storage.Storage.Entry; -import org.sonar.scanner.storage.Storages; - -/** - * Cache of all measures. This cache is shared amongst all project modules. - */ -public class MeasureCache { - - private final Storage<DefaultMeasure<?>> cache; - - public MeasureCache(Storages caches, MetricFinder metricFinder) { - caches.registerValueCoder(DefaultMeasure.class, new MeasureValueCoder(metricFinder)); - cache = caches.createCache("measures"); - } - - public Iterable<Entry<DefaultMeasure<?>>> entries() { - return cache.entries(); - } - - public Iterable<DefaultMeasure<?>> all() { - return cache.values(); - } - - public Iterable<DefaultMeasure<?>> byComponentKey(String effectiveKey) { - return cache.values(effectiveKey); - } - - @CheckForNull - public DefaultMeasure<?> byMetric(String componentKey, String metricKey) { - return cache.get(componentKey, metricKey); - } - - public MeasureCache put(String componentKey, String metricKey, DefaultMeasure<?> measure) { - Preconditions.checkNotNull(componentKey); - Preconditions.checkNotNull(metricKey); - cache.put(componentKey, metricKey, measure); - return this; - } - - public boolean contains(String componentKey, String metricKey) { - Preconditions.checkNotNull(componentKey); - Preconditions.checkNotNull(metricKey); - return cache.containsKey(componentKey, metricKey); - } - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureValueCoder.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureValueCoder.java deleted file mode 100644 index c7d87a1d2e0..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/measure/MeasureValueCoder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.scanner.scan.measure; - -import com.persistit.Value; -import com.persistit.encoding.CoderContext; -import com.persistit.encoding.ValueCoder; -import java.io.Serializable; -import org.sonar.api.batch.measure.MetricFinder; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; - -class MeasureValueCoder implements ValueCoder { - - private final MetricFinder metricFinder; - - public MeasureValueCoder(MetricFinder metricFinder) { - this.metricFinder = metricFinder; - } - - @Override - public void put(Value value, Object object, CoderContext context) { - DefaultMeasure<?> m = (DefaultMeasure<?>) object; - org.sonar.api.batch.measure.Metric<?> metric = m.metric(); - value.putString(metric.key()); - value.put(m.value()); - } - - @Override - public Object get(Value value, Class clazz, CoderContext context) { - String metricKey = value.getString(); - org.sonar.api.batch.measure.Metric<?> metric = metricFinder.findByKey(metricKey); - if (metric == null) { - throw new IllegalStateException("Unknow metric with key " + metricKey); - } - return new DefaultMeasure() - .forMetric(metric) - .withValue((Serializable) value.get()); - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java index ab27aa0b0fa..42965a37d98 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java @@ -19,21 +19,20 @@ */ package org.sonar.scanner.sensor; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; +import java.io.Serializable; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeMap; +import java.util.SortedMap; import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.Nullable; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputDir; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.fs.internal.DefaultInputComponent; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.measure.Metric; @@ -51,10 +50,12 @@ import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; import org.sonar.api.batch.sensor.rule.internal.DefaultAdHocRule; import org.sonar.api.batch.sensor.symbol.internal.DefaultSymbolTable; import org.sonar.api.config.Configuration; +import org.sonar.api.measures.CoreMetrics; import org.sonar.api.utils.KeyValueFormat; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.metric.ScannerMetrics; +import org.sonar.core.util.CloseableIterator; import org.sonar.duplications.block.Block; import org.sonar.duplications.internal.pmd.PmdBlockChunker; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; @@ -67,52 +68,15 @@ import org.sonar.scanner.report.ReportPublisher; import org.sonar.scanner.report.ScannerReportUtils; import org.sonar.scanner.repository.ContextPropertiesCache; import org.sonar.scanner.scan.branch.BranchConfiguration; -import org.sonar.scanner.scan.measure.MeasureCache; +import static java.lang.Math.max; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableSet; import static java.util.stream.Collectors.toList; -import static org.sonar.api.measures.CoreMetrics.BRANCH_COVERAGE; import static org.sonar.api.measures.CoreMetrics.COMMENT_LINES_DATA_KEY; -import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE; -import static org.sonar.api.measures.CoreMetrics.CONDITIONS_BY_LINE_KEY; -import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER; -import static org.sonar.api.measures.CoreMetrics.CONDITIONS_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.COVERAGE; -import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA; -import static org.sonar.api.measures.CoreMetrics.COVERAGE_LINE_HITS_DATA_KEY; -import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE; -import static org.sonar.api.measures.CoreMetrics.COVERED_CONDITIONS_BY_LINE_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_BRANCH_COVERAGE_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_BY_LINE_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_CONDITIONS_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_COVERAGE_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_COVERAGE_LINE_HITS_DATA_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_COVERED_CONDITIONS_BY_LINE_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_LINES_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_LINE_COVERAGE_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_CONDITIONS_KEY; -import static org.sonar.api.measures.CoreMetrics.IT_UNCOVERED_LINES_KEY; import static org.sonar.api.measures.CoreMetrics.LINES_KEY; -import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER; -import static org.sonar.api.measures.CoreMetrics.LINES_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.LINE_COVERAGE; -import static org.sonar.api.measures.CoreMetrics.OVERALL_BRANCH_COVERAGE_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_BY_LINE_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_CONDITIONS_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERAGE_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERAGE_LINE_HITS_DATA_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_COVERED_CONDITIONS_BY_LINE_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_LINES_TO_COVER_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_LINE_COVERAGE_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_CONDITIONS_KEY; -import static org.sonar.api.measures.CoreMetrics.OVERALL_UNCOVERED_LINES_KEY; import static org.sonar.api.measures.CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY_KEY; import static org.sonar.api.measures.CoreMetrics.TEST_SUCCESS_DENSITY_KEY; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_CONDITIONS_KEY; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES; -import static org.sonar.api.measures.CoreMetrics.UNCOVERED_LINES_KEY; public class DefaultSensorStorage implements SensorStorage { @@ -140,51 +104,9 @@ public class DefaultSensorStorage implements SensorStorage { TEST_SUCCESS_DENSITY_KEY, PUBLIC_DOCUMENTED_API_DENSITY_KEY))); - private static final Set<String> COVERAGE_METRIC_KEYS = unmodifiableSet(new HashSet<>(asList( - UNCOVERED_LINES_KEY, - LINES_TO_COVER_KEY, - UNCOVERED_CONDITIONS_KEY, - CONDITIONS_TO_COVER_KEY, - CONDITIONS_BY_LINE_KEY, - COVERED_CONDITIONS_BY_LINE_KEY, - COVERAGE_LINE_HITS_DATA_KEY))); - - private static final Set<String> COVERAGE_BY_LINE_METRIC_KEYS = unmodifiableSet(new HashSet<>(asList( - COVERAGE_LINE_HITS_DATA_KEY, - COVERED_CONDITIONS_BY_LINE_KEY, - CONDITIONS_BY_LINE_KEY))); - - private static final Map<String, Metric> DEPRECATED_COVERAGE_METRICS_MAPPING; - - static { - Map<String, Metric> map = new HashMap<>(); - map.put(IT_COVERAGE_KEY, COVERAGE); - map.put(IT_LINE_COVERAGE_KEY, LINE_COVERAGE); - map.put(IT_BRANCH_COVERAGE_KEY, BRANCH_COVERAGE); - map.put(IT_UNCOVERED_LINES_KEY, UNCOVERED_LINES); - map.put(IT_LINES_TO_COVER_KEY, LINES_TO_COVER); - map.put(IT_UNCOVERED_CONDITIONS_KEY, UNCOVERED_CONDITIONS); - map.put(IT_CONDITIONS_TO_COVER_KEY, CONDITIONS_TO_COVER); - map.put(IT_CONDITIONS_BY_LINE_KEY, CONDITIONS_BY_LINE); - map.put(IT_COVERED_CONDITIONS_BY_LINE_KEY, COVERED_CONDITIONS_BY_LINE); - map.put(IT_COVERAGE_LINE_HITS_DATA_KEY, COVERAGE_LINE_HITS_DATA); - map.put(OVERALL_COVERAGE_KEY, COVERAGE); - map.put(OVERALL_LINE_COVERAGE_KEY, LINE_COVERAGE); - map.put(OVERALL_BRANCH_COVERAGE_KEY, BRANCH_COVERAGE); - map.put(OVERALL_UNCOVERED_LINES_KEY, UNCOVERED_LINES); - map.put(OVERALL_LINES_TO_COVER_KEY, LINES_TO_COVER); - map.put(OVERALL_UNCOVERED_CONDITIONS_KEY, UNCOVERED_CONDITIONS); - map.put(OVERALL_CONDITIONS_TO_COVER_KEY, CONDITIONS_TO_COVER); - map.put(OVERALL_CONDITIONS_BY_LINE_KEY, CONDITIONS_BY_LINE); - map.put(OVERALL_COVERED_CONDITIONS_BY_LINE_KEY, COVERED_CONDITIONS_BY_LINE); - map.put(OVERALL_COVERAGE_LINE_HITS_DATA_KEY, COVERAGE_LINE_HITS_DATA); - DEPRECATED_COVERAGE_METRICS_MAPPING = Collections.unmodifiableMap(map); - } - private final MetricFinder metricFinder; private final IssuePublisher moduleIssues; private final ReportPublisher reportPublisher; - private final MeasureCache measureCache; private final SonarCpdBlockIndex index; private final ContextPropertiesCache contextPropertiesCache; private final Configuration settings; @@ -193,13 +115,12 @@ public class DefaultSensorStorage implements SensorStorage { private final Set<String> alreadyLogged = new HashSet<>(); public DefaultSensorStorage(MetricFinder metricFinder, IssuePublisher moduleIssues, Configuration settings, - ReportPublisher reportPublisher, MeasureCache measureCache, SonarCpdBlockIndex index, - ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics, BranchConfiguration branchConfiguration) { + ReportPublisher reportPublisher, SonarCpdBlockIndex index, + ContextPropertiesCache contextPropertiesCache, ScannerMetrics scannerMetrics, BranchConfiguration branchConfiguration) { this.metricFinder = metricFinder; this.moduleIssues = moduleIssues; this.settings = settings; this.reportPublisher = reportPublisher; - this.measureCache = measureCache; this.index = index; this.contextPropertiesCache = contextPropertiesCache; this.scannerMetrics = scannerMetrics; @@ -243,107 +164,48 @@ public class DefaultSensorStorage implements SensorStorage { return; } - DefaultMeasure measureToSave; - if (DEPRECATED_COVERAGE_METRICS_MAPPING.containsKey(metric.key())) { - metric = DEPRECATED_COVERAGE_METRICS_MAPPING.get(metric.key()); - measureToSave = new DefaultMeasure<>() - .forMetric(metric) - .on(measure.inputComponent()) - .withValue(measure.value()); - } else { - measureToSave = measure; - } - if (!scannerMetrics.getMetrics().contains(metric)) { throw new UnsupportedOperationException("Metric '" + metric.key() + "' should not be computed by a Sensor"); } - if (COVERAGE_METRIC_KEYS.contains(metric.key())) { - logOnce(metric.key(), "Coverage measure for metric '{}' should not be saved directly by a Sensor. Plugin should be updated to use SensorContext::newCoverage instead.", - metric.key()); - if (!component.isFile()) { - throw new UnsupportedOperationException("Saving coverage measure is only allowed on files. Attempt to save '" + metric.key() + "' on '" + component.key() + "'"); - } - if (((DefaultInputFile) component).isExcludedForCoverage()) { - return; - } - saveCoverageMetricInternal((InputFile) component, metric, measureToSave); - } else { - if (measureCache.contains(component.key(), metric.key())) { - throw new UnsupportedOperationException("Can not add the same measure twice on " + component + ": " + measure); - } - measureCache.put(component.key(), metric.key(), measureToSave); + if (((DefaultInputComponent) component).hasMeasureFor(metric)) { + throw new UnsupportedOperationException("Can not add the same measure twice on " + component + ": " + measure); } - } - - private void saveCoverageMetricInternal(InputFile file, Metric<?> metric, DefaultMeasure<?> measure) { - if (COVERAGE_BY_LINE_METRIC_KEYS.contains(metric.key())) { - validateCoverageMeasure((String) measure.value(), file); - DefaultMeasure<?> previousMeasure = measureCache.byMetric(file.key(), metric.key()); - if (previousMeasure != null) { - measureCache.put(file.key(), metric.key(), new DefaultMeasure<String>() - .forMetric((Metric<String>) metric) - .withValue(mergeCoverageLineMetric(metric, (String) previousMeasure.value(), (String) measure.value()))); + ((DefaultInputComponent) component).setHasMeasureFor(metric); + if (metric.key().equals(CoreMetrics.EXECUTABLE_LINES_DATA_KEY)) { + if (component.isFile()) { + ((DefaultInputFile) component).setExecutableLines( + KeyValueFormat.parseIntInt((String) measure.value()).entrySet().stream().filter(e -> e.getValue() > 0).map(Map.Entry::getKey).collect(Collectors.toSet())); } else { - measureCache.put(file.key(), metric.key(), measure); + throw new IllegalArgumentException("Executable lines can only be saved on files"); } } else { - // Other coverage metrics are all integer values. Just erase value, it will be recomputed at the end anyway - measureCache.put(file.key(), metric.key(), measure); + reportPublisher.getWriter().appendComponentMeasure(((DefaultInputComponent) component).scannerId(), toReportMeasure(measure)); } } - /** - * Merge the two line coverage data measures. For lines hits use the sum, and for conditions - * keep max value in case they both contains a value for the same line. - */ - static String mergeCoverageLineMetric(Metric<?> metric, String value1, String value2) { - Map<Integer, Integer> data1 = KeyValueFormat.parseIntInt(value1); - Map<Integer, Integer> data2 = KeyValueFormat.parseIntInt(value2); - if (metric.key().equals(COVERAGE_LINE_HITS_DATA_KEY)) { - return KeyValueFormat.format(Stream.of(data1, data2) - .map(Map::entrySet) - .flatMap(Collection::stream) - .collect( - Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - Integer::sum, - TreeMap::new))); - } else { - return KeyValueFormat.format(Stream.of(data1, data2) - .map(Map::entrySet) - .flatMap(Collection::stream) - .collect( - Collectors.toMap( - Map.Entry::getKey, - Map.Entry::getValue, - Integer::max, - TreeMap::new))); - } - } - - static void validateCoverageMeasure(String value, InputFile inputFile) { - Map<Integer, Integer> m = KeyValueFormat.parseIntInt(value); - validatePositiveLine(m, inputFile.toString()); - validateMaxLine(m, inputFile); - } - - private static void validateMaxLine(Map<Integer, Integer> m, InputFile inputFile) { - int maxLine = inputFile.lines(); - - for (int line : m.keySet()) { - if (line > maxLine) { - throw new IllegalStateException(String.format("Can't create measure for line %d for file '%s' with %d lines", line, inputFile, maxLine)); - } - } + public static ScannerReport.Measure toReportMeasure(DefaultMeasure measureToSave) { + ScannerReport.Measure.Builder builder = ScannerReport.Measure.newBuilder(); + builder.setMetricKey(measureToSave.metric().key()); + setValueAccordingToType(builder, measureToSave); + return builder.build(); } - private static void validatePositiveLine(Map<Integer, Integer> m, String filePath) { - for (int l : m.keySet()) { - if (l <= 0) { - throw new IllegalStateException(String.format("Measure with line %d for file '%s' must be > 0", l, filePath)); - } + private static void setValueAccordingToType(ScannerReport.Measure.Builder builder, DefaultMeasure<?> measure) { + Serializable value = measure.value(); + Metric<?> metric = measure.metric(); + if (Boolean.class.equals(metric.valueType())) { + builder.setBooleanValue(ScannerReport.Measure.BoolValue.newBuilder().setValue((Boolean) value)); + } else if (Integer.class.equals(metric.valueType())) { + builder.setIntValue(ScannerReport.Measure.IntValue.newBuilder().setValue(((Number) value).intValue())); + } else if (Double.class.equals(metric.valueType())) { + builder.setDoubleValue(ScannerReport.Measure.DoubleValue.newBuilder().setValue(((Number) value).doubleValue())); + } else if (String.class.equals(metric.valueType())) { + builder.setStringValue(ScannerReport.Measure.StringValue.newBuilder().setValue((String) value)); + } else if (Long.class.equals(metric.valueType())) { + builder.setLongValue(ScannerReport.Measure.LongValue.newBuilder().setValue(((Number) value).longValue())); + } else { + throw new UnsupportedOperationException("Unsupported type :" + metric.valueType()); } } @@ -462,22 +324,51 @@ public class DefaultSensorStorage implements SensorStorage { public void store(DefaultCoverage defaultCoverage) { DefaultInputFile inputFile = (DefaultInputFile) defaultCoverage.inputFile(); inputFile.setPublished(true); - if (defaultCoverage.linesToCover() > 0) { - saveCoverageMetricInternal(inputFile, LINES_TO_COVER, new DefaultMeasure<Integer>().forMetric(LINES_TO_COVER).withValue(defaultCoverage.linesToCover())); - saveCoverageMetricInternal(inputFile, UNCOVERED_LINES, - new DefaultMeasure<Integer>().forMetric(UNCOVERED_LINES).withValue(defaultCoverage.linesToCover() - defaultCoverage.coveredLines())); - saveCoverageMetricInternal(inputFile, COVERAGE_LINE_HITS_DATA, - new DefaultMeasure<String>().forMetric(COVERAGE_LINE_HITS_DATA).withValue(KeyValueFormat.format(defaultCoverage.hitsByLine()))); + + Map<Integer, ScannerReport.LineCoverage.Builder> coveragePerLine = reloadExistingCoverage(inputFile); + + int lineCount = inputFile.lines(); + mergeLineCoverageValues(lineCount, defaultCoverage.hitsByLine(), coveragePerLine, (value, builder) -> builder.setHits(builder.getHits() || value > 0)); + mergeLineCoverageValues(lineCount, defaultCoverage.conditionsByLine(), coveragePerLine, (value, builder) -> builder.setConditions(max(value, builder.getConditions()))); + mergeLineCoverageValues(lineCount, defaultCoverage.coveredConditionsByLine(), coveragePerLine, + (value, builder) -> builder.setCoveredConditions(max(value, builder.getCoveredConditions()))); + + reportPublisher.getWriter().writeComponentCoverage(inputFile.scannerId(), + coveragePerLine.values().stream().map(ScannerReport.LineCoverage.Builder::build).collect(Collectors.toList())); + + } + + private Map<Integer, ScannerReport.LineCoverage.Builder> reloadExistingCoverage(DefaultInputFile inputFile) { + Map<Integer, ScannerReport.LineCoverage.Builder> coveragePerLine = new LinkedHashMap<>(); + try (CloseableIterator<ScannerReport.LineCoverage> lineCoverageCloseableIterator = reportPublisher.getReader().readComponentCoverage(inputFile.scannerId())) { + while (lineCoverageCloseableIterator.hasNext()) { + final ScannerReport.LineCoverage lineCoverage = lineCoverageCloseableIterator.next(); + coveragePerLine.put(lineCoverage.getLine(), ScannerReport.LineCoverage.newBuilder(lineCoverage)); + } + } + return coveragePerLine; + } + + interface LineCoverageOperation { + void apply(Integer value, ScannerReport.LineCoverage.Builder builder); + } + + private void mergeLineCoverageValues(int lineCount, SortedMap<Integer, Integer> valueByLine, Map<Integer, ScannerReport.LineCoverage.Builder> coveragePerLine, + LineCoverageOperation op) { + for (Map.Entry<Integer, Integer> lineMeasure : valueByLine.entrySet()) { + int lineIdx = lineMeasure.getKey(); + if (lineIdx <= lineCount) { + Integer value = lineMeasure.getValue(); + op.apply(value, coveragePerLine.computeIfAbsent(lineIdx, line -> ScannerReport.LineCoverage.newBuilder().setLine(line))); + } } - if (defaultCoverage.conditions() > 0) { - saveCoverageMetricInternal(inputFile, CONDITIONS_TO_COVER, - new DefaultMeasure<Integer>().forMetric(CONDITIONS_TO_COVER).withValue(defaultCoverage.conditions())); - saveCoverageMetricInternal(inputFile, UNCOVERED_CONDITIONS, - new DefaultMeasure<Integer>().forMetric(UNCOVERED_CONDITIONS).withValue(defaultCoverage.conditions() - defaultCoverage.coveredConditions())); - saveCoverageMetricInternal(inputFile, COVERED_CONDITIONS_BY_LINE, - new DefaultMeasure<String>().forMetric(COVERED_CONDITIONS_BY_LINE).withValue(KeyValueFormat.format(defaultCoverage.coveredConditionsByLine()))); - saveCoverageMetricInternal(inputFile, CONDITIONS_BY_LINE, - new DefaultMeasure<String>().forMetric(CONDITIONS_BY_LINE).withValue(KeyValueFormat.format(defaultCoverage.conditionsByLine()))); + } + + private static void validatePositiveLine(Map<Integer, Integer> m, String filePath) { + for (int l : m.keySet()) { + if (l <= 0) { + throw new IllegalStateException(String.format("Measure with line %d for file '%s' must be > 0", l, filePath)); + } } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java index ffec3a709ef..f549ec6216c 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/source/ZeroCoverageSensor.java @@ -19,41 +19,30 @@ */ package org.sonar.scanner.source; -import com.google.common.collect.Sets; -import java.util.Map; import java.util.Set; -import java.util.stream.StreamSupport; import org.sonar.api.batch.Phase; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.InputFile.Type; import org.sonar.api.batch.fs.internal.DefaultInputFile; -import org.sonar.api.batch.measure.Metric; -import org.sonar.api.batch.sensor.Sensor; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.SensorDescriptor; -import org.sonar.api.batch.sensor.coverage.CoverageType; import org.sonar.api.batch.sensor.coverage.NewCoverage; -import org.sonar.api.batch.sensor.measure.internal.DefaultMeasure; -import org.sonar.api.measures.CoreMetrics; -import org.sonar.api.utils.KeyValueFormat; -import org.sonar.scanner.scan.measure.MeasureCache; - -import static org.sonar.core.util.stream.MoreCollectors.toSet; +import org.sonar.api.scanner.sensor.ProjectSensor; +import org.sonar.scanner.report.ReportPublisher; @Phase(name = Phase.Name.POST) -public final class ZeroCoverageSensor implements Sensor { +public final class ZeroCoverageSensor implements ProjectSensor { - private final MeasureCache measureCache; + private final ReportPublisher reportPublisher; - public ZeroCoverageSensor(MeasureCache measureCache) { - this.measureCache = measureCache; + public ZeroCoverageSensor(ReportPublisher reportPublisher) { + this.reportPublisher = reportPublisher; } @Override public void describe(SensorDescriptor descriptor) { - descriptor.name("Zero Coverage Sensor") - .global(); + descriptor.name("Zero Coverage Sensor"); } @Override @@ -63,32 +52,26 @@ public final class ZeroCoverageSensor implements Sensor { if (((DefaultInputFile) f).isExcludedForCoverage()) { continue; } - if (!isCoverageMeasuresAlreadyDefined(f)) { - DefaultMeasure<String> execLines = (DefaultMeasure<String>) measureCache.byMetric(f.key(), CoreMetrics.EXECUTABLE_LINES_DATA_KEY); - if (execLines != null) { + if (!isCoverageAlreadyDefined(f)) { + ((DefaultInputFile) f).getExecutableLines().ifPresent(execLines -> { storeZeroCoverageForEachExecutableLine(context, f, execLines); - } + }); } } } - private static void storeZeroCoverageForEachExecutableLine(final SensorContext context, InputFile f, DefaultMeasure<String> execLines) { + private static void storeZeroCoverageForEachExecutableLine(final SensorContext context, InputFile f, Set<Integer> executableLines) { NewCoverage newCoverage = context.newCoverage().onFile(f); - Map<Integer, Integer> lineMeasures = KeyValueFormat.parseIntInt((String) execLines.value()); - for (Map.Entry<Integer, Integer> lineMeasure : lineMeasures.entrySet()) { - int lineIdx = lineMeasure.getKey(); - if (lineIdx <= f.lines() && lineMeasure.getValue() > 0) { + for (Integer lineIdx : executableLines) { + if (lineIdx <= f.lines()) { newCoverage.lineHits(lineIdx, 0); } } newCoverage.save(); } - private boolean isCoverageMeasuresAlreadyDefined(InputFile f) { - Set<String> metricKeys = StreamSupport.stream(measureCache.byComponentKey(f.key()).spliterator(), false) - .map(m -> m.metric().key()).collect(toSet()); - Set<String> allCoverageMetricKeys = CoverageType.UNIT.allMetrics().stream().map(Metric::key).collect(toSet()); - return !Sets.intersection(metricKeys, allCoverageMetricKeys).isEmpty(); + private boolean isCoverageAlreadyDefined(InputFile f) { + return reportPublisher.getReader().hasCoverage(((DefaultInputFile) f).scannerId()); } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java deleted file mode 100644 index c7b64639b44..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storage.java +++ /dev/null @@ -1,500 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.scanner.storage; - -import com.persistit.Exchange; -import com.persistit.Key; -import com.persistit.KeyFilter; -import com.persistit.exception.PersistitException; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.NoSuchElementException; -import java.util.Set; -import javax.annotation.CheckForNull; -import org.apache.commons.lang.builder.ToStringBuilder; - -/** - * <p> - * This storage is not thread-safe, due to direct usage of {@link com.persistit.Exchange} - * </p> - */ -public class Storage<V> { - - private final String name; - private final Exchange exchange; - - Storage(String name, Exchange exchange) { - this.name = name; - this.exchange = exchange; - } - - public Storage<V> put(Object key, V value) { - resetKey(key); - return doPut(value); - } - - public Storage<V> put(Object firstKey, Object secondKey, V value) { - resetKey(firstKey, secondKey); - return doPut(value); - } - - public Storage<V> put(Object firstKey, Object secondKey, Object thirdKey, V value) { - resetKey(firstKey, secondKey, thirdKey); - return doPut(value); - } - - public Storage<V> put(Object[] key, V value) { - resetKey(key); - return doPut(value); - } - - private Storage<V> doPut(V value) { - try { - exchange.getValue().put(value); - exchange.store(); - return this; - } catch (Exception e) { - throw new IllegalStateException("Fail to put element in the storage '" + name + "'", e); - } - } - - /** - * Returns the value object associated with keys, or null if not found. - */ - public V get(Object key) { - resetKey(key); - return doGet(); - } - - /** - * Returns the value object associated with keys, or null if not found. - */ - @CheckForNull - public V get(Object firstKey, Object secondKey) { - resetKey(firstKey, secondKey); - return doGet(); - } - - /** - * Returns the value object associated with keys, or null if not found. - */ - @CheckForNull - public V get(Object firstKey, Object secondKey, Object thirdKey) { - resetKey(firstKey, secondKey, thirdKey); - return doGet(); - } - - /** - * Returns the value object associated with keys, or null if not found. - */ - @CheckForNull - public V get(Object[] key) { - resetKey(key); - return doGet(); - } - - @SuppressWarnings("unchecked") - @CheckForNull - private V doGet() { - try { - exchange.fetch(); - if (!exchange.getValue().isDefined()) { - return null; - } - return (V) exchange.getValue().get(); - } catch (Exception e) { - // TODO add parameters to message - throw new IllegalStateException("Fail to get element from cache " + name, e); - } - } - - public boolean containsKey(Object key) { - resetKey(key); - return doContainsKey(); - } - - public boolean containsKey(Object firstKey, Object secondKey) { - resetKey(firstKey, secondKey); - return doContainsKey(); - } - - public boolean containsKey(Object firstKey, Object secondKey, Object thirdKey) { - resetKey(firstKey, secondKey, thirdKey); - return doContainsKey(); - } - - public boolean containsKey(Object[] key) { - resetKey(key); - return doContainsKey(); - } - - private boolean doContainsKey() { - try { - exchange.fetch(); - return exchange.isValueDefined(); - } catch (Exception e) { - // TODO add parameters to message - throw new IllegalStateException("Fail to check if element is in cache " + name, e); - } - } - - public boolean remove(Object key) { - resetKey(key); - return doRemove(); - } - - public boolean remove(Object firstKey, Object secondKey) { - resetKey(firstKey, secondKey); - return doRemove(); - } - - public boolean remove(Object firstKey, Object secondKey, Object thirdKey) { - resetKey(firstKey, secondKey, thirdKey); - return doRemove(); - } - - public boolean remove(Object[] key) { - resetKey(key); - return doRemove(); - } - - private boolean doRemove() { - try { - return exchange.remove(); - } catch (Exception e) { - // TODO add parameters to message - throw new IllegalStateException("Fail to get element from cache " + name, e); - } - } - - /** - * Removes everything in the specified group. - */ - public Storage<V> clear(Object key) { - resetKey(key); - return doClear(); - } - - public Storage<V> clear(Object firstKey, Object secondKey) { - resetKey(firstKey, secondKey); - return doClear(); - } - - public Storage<V> clear(Object firstKey, Object secondKey, Object thirdKey) { - resetKey(firstKey, secondKey, thirdKey); - return doClear(); - } - - public Storage<V> clear(Object[] key) { - resetKey(key); - return doClear(); - } - - private Storage<V> doClear() { - try { - Key to = new Key(exchange.getKey()); - to.append(Key.AFTER); - exchange.removeKeyRange(exchange.getKey(), to); - return this; - } catch (Exception e) { - throw new IllegalStateException("Fail to clear values from cache " + name, e); - } - } - - /** - * Clears the default as well as all group caches. - */ - public void clear() { - try { - exchange.clear(); - exchange.removeAll(); - } catch (Exception e) { - throw new IllegalStateException("Fail to clear cache", e); - } - } - - /** - * Returns the set of cache keys associated with this group. - * TODO implement a lazy-loading equivalent with Iterator/Iterable - * - * @return The set of cache keys for this group. - */ - @SuppressWarnings("rawtypes") - public Set keySet(Object key) { - try { - Set<Object> keys = new LinkedHashSet<>(); - exchange.clear(); - Exchange iteratorExchange = new Exchange(exchange); - iteratorExchange.append(key); - iteratorExchange.append(Key.BEFORE); - while (iteratorExchange.next(false)) { - keys.add(iteratorExchange.getKey().indexTo(-1).decode()); - } - return keys; - } catch (Exception e) { - throw new IllegalStateException("Fail to get keys from cache " + name, e); - } - } - - @SuppressWarnings("rawtypes") - public Set keySet(Object firstKey, Object secondKey) { - try { - Set<Object> keys = new LinkedHashSet<>(); - exchange.clear(); - Exchange iteratorExchange = new Exchange(exchange); - iteratorExchange.append(firstKey); - iteratorExchange.append(secondKey); - iteratorExchange.append(Key.BEFORE); - while (iteratorExchange.next(false)) { - keys.add(iteratorExchange.getKey().indexTo(-1).decode()); - } - return keys; - } catch (Exception e) { - throw new IllegalStateException("Fail to get keys from cache " + name, e); - } - } - - /** - * Returns the set of keys associated with this cache. - * - * @return The set containing the keys for this cache. - */ - public Set<Object> keySet() { - try { - Set<Object> keys = new LinkedHashSet<>(); - exchange.clear(); - Exchange iteratorExchange = new Exchange(exchange); - iteratorExchange.append(Key.BEFORE); - while (iteratorExchange.next(false)) { - keys.add(iteratorExchange.getKey().indexTo(-1).decode()); - } - return keys; - } catch (Exception e) { - throw new IllegalStateException("Fail to get keys from cache " + name, e); - } - } - - /** - * Lazy-loading values for given keys - */ - public Iterable<V> values(Object firstKey, Object secondKey) { - return new ValueIterable<>(exchange, firstKey, secondKey); - } - - /** - * Lazy-loading values for a given key - */ - public Iterable<V> values(Object firstKey) { - return new ValueIterable<>(exchange, firstKey); - } - - /** - * Lazy-loading values - */ - public Iterable<V> values() { - return new ValueIterable<>(exchange); - } - - public Iterable<Entry<V>> entries() { - return new EntryIterable<>(exchange); - } - - public Iterable<Entry<V>> entries(Object firstKey) { - return new EntryIterable<>(exchange, firstKey); - } - - private void resetKey(Object key) { - exchange.clear(); - exchange.append(key); - } - - private void resetKey(Object first, Object second) { - exchange.clear(); - exchange.append(first).append(second); - } - - private void resetKey(Object first, Object second, Object third) { - exchange.clear(); - exchange.append(first).append(second).append(third); - } - - private void resetKey(Object[] keys) { - exchange.clear(); - for (Object o : keys) { - exchange.append(o); - } - } - - // - // LAZY ITERATORS AND ITERABLES - // - - private static class ValueIterable<T> implements Iterable<T> { - private final Exchange originExchange; - private final Object[] keys; - - private ValueIterable(Exchange originExchange, Object... keys) { - this.originExchange = originExchange; - this.keys = keys; - } - - @Override - public Iterator<T> iterator() { - originExchange.clear(); - KeyFilter filter = new KeyFilter(); - for (Object key : keys) { - originExchange.append(key); - filter = filter.append(KeyFilter.simpleTerm(key)); - } - originExchange.append(Key.BEFORE); - Exchange iteratorExchange = new Exchange(originExchange); - return new ValueIterator<>(iteratorExchange, filter); - } - } - - private static class ValueIterator<T> implements Iterator<T> { - private final Exchange exchange; - private final KeyFilter keyFilter; - - private ValueIterator(Exchange exchange, KeyFilter keyFilter) { - this.exchange = exchange; - this.keyFilter = keyFilter; - } - - @Override - public boolean hasNext() { - try { - return exchange.hasNext(keyFilter); - } catch (PersistitException e) { - throw new IllegalStateException(e); - } - } - - @SuppressWarnings("unchecked") - @Override - public T next() { - try { - exchange.next(keyFilter); - } catch (PersistitException e) { - throw new IllegalStateException(e); - } - if (exchange.getValue().isDefined()) { - return (T) exchange.getValue().get(); - } - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Removing an item is not supported"); - } - } - - private static class EntryIterable<T> implements Iterable<Entry<T>> { - private final Exchange originExchange; - private final Object[] keys; - - private EntryIterable(Exchange originExchange, Object... keys) { - this.originExchange = originExchange; - this.keys = keys; - } - - @Override - public Iterator<Entry<T>> iterator() { - originExchange.clear(); - KeyFilter filter = new KeyFilter(); - for (Object key : keys) { - originExchange.append(key); - filter = filter.append(KeyFilter.simpleTerm(key)); - } - originExchange.append(Key.BEFORE); - Exchange iteratorExchange = new Exchange(originExchange); - return new EntryIterator<>(iteratorExchange, filter); - } - } - - private static class EntryIterator<T> implements Iterator<Entry<T>> { - private final Exchange exchange; - private final KeyFilter keyFilter; - - private EntryIterator(Exchange exchange, KeyFilter keyFilter) { - this.exchange = exchange; - this.keyFilter = keyFilter; - } - - @Override - public boolean hasNext() { - try { - return exchange.hasNext(keyFilter); - } catch (PersistitException e) { - throw new IllegalStateException(e); - } - } - - @SuppressWarnings("unchecked") - @Override - public Entry<T> next() { - try { - exchange.next(keyFilter); - } catch (PersistitException e) { - throw new IllegalStateException(e); - } - if (exchange.getValue().isDefined()) { - T value = (T) exchange.getValue().get(); - Key key = exchange.getKey(); - Object[] array = new Object[key.getDepth()]; - for (int i = 0; i < key.getDepth(); i++) { - array[i] = key.indexTo(i - key.getDepth()).decode(); - } - return new Entry<>(array, value); - } - throw new NoSuchElementException(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Removing an item is not supported"); - } - } - - public static class Entry<V> { - private final Object[] key; - private final V value; - - Entry(Object[] key, V value) { - this.key = key; - this.value = value; - } - - public Object[] key() { - return key; - } - - public V value() { - return value; - } - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this); - } - } - -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storages.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storages.java deleted file mode 100644 index 23827ebe5d0..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/Storages.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.scanner.storage; - -import com.google.common.base.Preconditions; -import com.persistit.Exchange; -import com.persistit.Persistit; -import com.persistit.Value; -import com.persistit.Volume; -import com.persistit.encoding.CoderManager; -import com.persistit.encoding.ValueCoder; -import com.persistit.exception.PersistitException; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import org.picocontainer.Startable; - -public class Storages implements Startable { - private final Map<String, Exchange> cacheMap = new HashMap<>(); - private Persistit persistit; - private Volume volume; - - public Storages(StoragesManager storagesManager) { - persistit = storagesManager.persistit(); - doStart(); - } - - @Override - public void start() { - // done in constructor - } - - private void doStart() { - try { - persistit.flush(); - volume = persistit.createTemporaryVolume(); - } catch (Exception e) { - throw new IllegalStateException("Fail to create a cache volume", e); - } - } - - public void registerValueCoder(Class<?> clazz, ValueCoder coder) { - CoderManager cm = persistit.getCoderManager(); - cm.registerValueCoder(clazz, coder); - } - - public <V> Storage<V> createCache(String cacheName) { - Preconditions.checkState(volume != null && volume.isOpened(), "Caches are not initialized"); - Preconditions.checkState(!cacheMap.containsKey(cacheName), "Cache is already created: %s", cacheName); - try { - Exchange exchange = persistit.getExchange(volume, cacheName, true); - exchange.setMaximumValueSize(Value.MAXIMUM_SIZE); - Storage<V> cache = new Storage<>(cacheName, exchange); - cacheMap.put(cacheName, exchange); - return cache; - } catch (Exception e) { - throw new IllegalStateException("Fail to create cache: " + cacheName, e); - } - } - - @Override - public void stop() { - for (Entry<String, Exchange> e : cacheMap.entrySet()) { - persistit.releaseExchange(e.getValue()); - } - - cacheMap.clear(); - - if (volume != null) { - try { - volume.close(); - volume.delete(); - } catch (PersistitException e) { - throw new IllegalStateException("Fail to close caches", e); - } - volume = null; - } - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/StoragesManager.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/StoragesManager.java deleted file mode 100644 index 3f1b1d9dcbe..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/StoragesManager.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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.scanner.storage; - -import com.persistit.Persistit; -import com.persistit.exception.PersistitException; -import com.persistit.logging.Slf4jAdapter; -import java.io.File; -import java.util.Properties; -import org.picocontainer.Startable; -import org.slf4j.LoggerFactory; -import org.sonar.api.utils.TempFolder; - -import static org.sonar.core.util.FileUtils.deleteQuietly; - -/** - * Factory of storages - * - * @since 3.6 - */ -public class StoragesManager implements Startable { - private File tempDir; - private Persistit persistit; - private final TempFolder tempFolder; - - public StoragesManager(TempFolder tempFolder) { - this.tempFolder = tempFolder; - initPersistit(); - } - - private void initPersistit() { - try { - tempDir = tempFolder.newDir("caches"); - persistit = new Persistit(); - persistit.setPersistitLogger(new Slf4jAdapter(LoggerFactory.getLogger("PERSISTIT"))); - Properties props = new Properties(); - props.setProperty("datapath", tempDir.getAbsolutePath()); - props.setProperty("logpath", "${datapath}/log"); - props.setProperty("logfile", "${logpath}/persistit_${timestamp}.log"); - props.setProperty("buffer.count.8192", "10"); - props.setProperty("journalpath", "${datapath}/journal"); - props.setProperty("tmpvoldir", "${datapath}"); - props.setProperty("volume.1", "${datapath}/persistit,create,pageSize:8192,initialPages:10,extensionPages:100,maximumPages:25000"); - props.setProperty("jmx", "false"); - persistit.setProperties(props); - persistit.initialize(); - - } catch (Exception e) { - throw new IllegalStateException("Fail to start caches", e); - } - } - - @Override - public void start() { - // already started in constructor - } - - @Override - public void stop() { - if (persistit != null) { - try { - persistit.close(false); - persistit = null; - } catch (PersistitException e) { - throw new IllegalStateException("Fail to close caches", e); - } - } - deleteQuietly(tempDir); - tempDir = null; - } - - File tempDir() { - return tempDir; - } - - Persistit persistit() { - return persistit; - } -} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/package-info.java deleted file mode 100644 index 8187995b887..00000000000 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/storage/package-info.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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. - * - * This program 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. - */ -@ParametersAreNonnullByDefault -package org.sonar.scanner.storage; - -import javax.annotation.ParametersAreNonnullByDefault; |