From 101e804c19f0ad0d70ee7bec43203948e5deb135 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Thu, 11 Jun 2015 14:26:49 +0200 Subject: [PATCH] SONAR-6620 MetricRepository now loads all metrics at startup --- .../computation/metric/MetricDtoToMetric.java | 36 ++++++ .../metric/MetricRepositoryImpl.java | 76 +++++++++--- .../metric/MetricDtoToMetricTest.java | 63 ++++++++++ .../metric/MetricRepositoryImplTest.java | 108 +++++++++++------- .../MetricRepositoryImplTest/shared.xml | 7 ++ 5 files changed, 233 insertions(+), 57 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricDtoToMetric.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricDtoToMetricTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/metric/MetricRepositoryImplTest/shared.xml diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricDtoToMetric.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricDtoToMetric.java new file mode 100644 index 00000000000..39c2860458a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricDtoToMetric.java @@ -0,0 +1,36 @@ +/* + * 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 javax.annotation.Nonnull; +import org.sonar.core.metric.db.MetricDto; + +enum MetricDtoToMetric implements Function { + INSTANCE; + + @Override + @Nonnull + public Metric apply(@Nonnull MetricDto metricDto) { + return new MetricImpl( + metricDto.getId(), metricDto.getKey(), metricDto.getShortName(), + Metric.MetricType.valueOf(metricDto.getValueType())); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java index 4e04de2b2af..3307bc40cb9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/metric/MetricRepositoryImpl.java @@ -19,47 +19,91 @@ */ package org.sonar.server.computation.metric; +import com.google.common.base.Function; +import java.util.List; +import java.util.Map; +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import org.picocontainer.Startable; import org.sonar.core.metric.db.MetricDto; import org.sonar.core.persistence.DbSession; import org.sonar.server.db.DbClient; +import static com.google.common.collect.FluentIterable.from; import static java.util.Objects.requireNonNull; -public class MetricRepositoryImpl implements MetricRepository { +public class MetricRepositoryImpl implements MetricRepository, Startable { private final DbClient dbClient; + @CheckForNull + private Map metricsByKey; + @CheckForNull + private Map metricsById; public MetricRepositoryImpl(DbClient dbClient) { this.dbClient = dbClient; } + @Override + public void start() { + try (DbSession dbSession = dbClient.openSession(false)) { + List metricList = dbClient.metricDao().selectEnabled(dbSession); + this.metricsByKey = from(metricList).transform(MetricDtoToMetric.INSTANCE).uniqueIndex(MetricToKey.INSTANCE); + this.metricsById = from(metricList).transform(MetricDtoToMetric.INSTANCE).uniqueIndex(MetricToId.INSTANCE); + } + } + + @Override + public void stop() { + // nothing to do when stopping + } + @Override public Metric getByKey(String key) { requireNonNull(key); + verifyMetricsInitialized(); - try (DbSession dbSession = dbClient.openSession(false)) { - MetricDto metricDto = dbClient.metricDao().selectNullableByKey(dbSession, key); - if (metricDto == null) { - throw new IllegalStateException(String.format("Metric with key '%s' does not exist", key)); - } - - return toMetric(metricDto); + Metric res = this.metricsByKey.get(key); + if (res == null) { + throw new IllegalStateException(String.format("Metric with key '%s' does not exist", key)); } + return res; } @Override public Metric getById(long id) { - try (DbSession dbSession = dbClient.openSession(false)) { - MetricDto metricDto = dbClient.metricDao().selectNullableById(dbSession, id); - if (metricDto == null) { - throw new IllegalStateException(String.format("Metric with id '%s' does not exist", id)); - } + verifyMetricsInitialized(); + + Metric res = this.metricsById.get(id); + if (res == null) { + throw new IllegalStateException(String.format("Metric with id '%s' does not exist", id)); + } + return res; + } + + private void verifyMetricsInitialized() { + if (this.metricsByKey == null) { + throw new IllegalStateException("Metric cache has not been initialized"); + } + } + + private enum MetricToKey implements Function { + INSTANCE; - return toMetric(metricDto); + @Override + @Nonnull + public String apply(@Nonnull Metric metric) { + return metric.getKey(); } } - private static Metric toMetric(MetricDto metricDto) { - return new MetricImpl(metricDto.getId(), metricDto.getKey(), metricDto.getShortName(), Metric.MetricType.valueOf(metricDto.getValueType())); + private enum MetricToId implements Function { + INSTANCE; + + @Override + @Nonnull + public Long apply(@Nonnull Metric metric) { + return (long) metric.getId(); + } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricDtoToMetricTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricDtoToMetricTest.java new file mode 100644 index 00000000000..9bca629f2f8 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricDtoToMetricTest.java @@ -0,0 +1,63 @@ +/* + * 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.junit.Test; +import org.sonar.core.metric.db.MetricDto; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MetricDtoToMetricTest { + + private MetricDtoToMetric underTest = MetricDtoToMetric.INSTANCE; + + @Test(expected = NullPointerException.class) + public void apply_throws_NPE_if_arg_is_null() { + underTest.apply(null); + } + + @Test + public void verify_mapping_from_dto() { + + for (Metric.MetricType metricType : Metric.MetricType.values()) { + MetricDto metricDto = createMetricDto(metricType); + Metric metric = underTest.apply(metricDto); + + assertThat(metric.getId()).isEqualTo(metricDto.getId()); + assertThat(metric.getKey()).isEqualTo(metricDto.getKey()); + assertThat(metric.getName()).isEqualTo(metricDto.getShortName()); + assertThat(metric.getType()).isEqualTo(metricType); + } + } + + @Test(expected = IllegalArgumentException.class) + public void apply_throws_IAE_if_valueType_can_not_be_parsed() { + underTest.apply(new MetricDto().setId(1).setKey("key").setValueType("trololo")); + } + + private static MetricDto createMetricDto(Metric.MetricType metricType) { + return new MetricDto() + .setId(metricType.name().hashCode()) + .setKey(metricType.name() + "_key") + .setShortName(metricType.name() + "_name") + .setValueType(metricType.name()) + .setEnabled(true); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java index e47c5adc2d4..e37b02cae19 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/metric/MetricRepositoryImplTest.java @@ -19,14 +19,12 @@ */ package org.sonar.server.computation.metric; -import javax.annotation.CheckForNull; -import org.junit.After; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.sonar.core.metric.db.MetricDto; -import org.sonar.core.persistence.DbSession; +import org.junit.rules.ExpectedException; import org.sonar.core.persistence.DbTester; import org.sonar.server.db.DbClient; import org.sonar.server.metric.persistence.MetricDao; @@ -36,71 +34,99 @@ import static org.assertj.core.api.Assertions.assertThat; @Category(DbTests.class) public class MetricRepositoryImplTest { - private static final String SOME_KEY = "some key"; - private static final String SOME_NAME = "the short name"; + private static final String SOME_KEY = "some_key"; + private static final long SOME_ID = 156; @ClassRule public static final DbTester dbTester = new DbTester(); + @Rule + public final ExpectedException expectedException = ExpectedException.none(); private DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new MetricDao()); - private MetricRepository underTest = new MetricRepositoryImpl(dbClient); - - @CheckForNull - private DbSession dbSession; + private MetricRepositoryImpl underTest = new MetricRepositoryImpl(dbClient); @Before public void setUp() throws Exception { dbTester.truncateTables(); } - @After - public void tearDown() throws Exception { - if (dbSession != null) { - dbSession.close(); - } - } - @Test(expected = NullPointerException.class) - public void findByKey_throws_NPE_if_arg_is_null() { + public void getByKey_throws_NPE_if_arg_is_null() { underTest.getByKey(null); } - @Test(expected = IllegalStateException.class) - public void findByKey_throws_ISE_of_Metric_does_not_exist() { + @Test + public void getByKey_throws_ISE_if_start_has_not_been_called() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Metric cache has not been initialized"); + underTest.getByKey(SOME_KEY); } @Test - public void verify_mapping_and_valueType_conversion_from_DB() { - dbSession = dbClient.openSession(false); + public void getByKey_throws_ISE_of_Metric_does_not_exist() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage(String.format("Metric with key '%s' does not exist", SOME_KEY)); - for (Metric.MetricType metricType : Metric.MetricType.values()) { - verify_mapping_and_valueType_conversion_from_DB_impl(metricType.name(), metricType); - } + underTest.start(); + underTest.getByKey(SOME_KEY); } - private void verify_mapping_and_valueType_conversion_from_DB_impl(String valueType, Metric.MetricType expected) { - MetricDto metricDto = new MetricDto().setId(SOME_KEY.hashCode()).setKey(SOME_KEY + valueType).setShortName(SOME_NAME).setValueType(valueType); + @Test + public void getByKey_throws_ISE_of_Metric_is_disabled() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage(String.format("Metric with key '%s' does not exist", "complexity")); - dbClient.metricDao().insert(dbSession, metricDto); - dbSession.commit(); + dbTester.prepareDbUnit(getClass(), "shared.xml"); - Metric metric = underTest.getByKey(metricDto.getKey()); + underTest.start(); + underTest.getByKey("complexity"); + } + + @Test + public void getByKey_find_enabled_Metrics() { + dbTester.prepareDbUnit(getClass(), "shared.xml"); - assertThat(metric.getId()).isEqualTo(metricDto.getId()); - assertThat(metric.getKey()).isEqualTo(metricDto.getKey()); - assertThat(metric.getName()).isEqualTo(metricDto.getShortName()); - assertThat(metric.getType()).isEqualTo(expected); + underTest.start(); + assertThat(underTest.getByKey("ncloc").getId()).isEqualTo(1); + assertThat(underTest.getByKey("coverage").getId()).isEqualTo(2); } - @Test(expected = IllegalArgumentException.class) - public void findByKey_throws_IAE_if_valueType_can_not_be_parsed() { - MetricDto metricDto = new MetricDto().setKey(SOME_KEY).setShortName(SOME_NAME).setValueType("trololo"); + @Test + public void getById_throws_ISE_if_start_has_not_been_called() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage("Metric cache has not been initialized"); - dbSession = dbClient.openSession(false); - dbClient.metricDao().insert(dbSession, metricDto); - dbSession.commit(); + underTest.getById(SOME_ID); + } - underTest.getByKey(SOME_KEY); + @Test + public void getById_throws_ISE_of_Metric_does_not_exist() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage(String.format("Metric with id '%s' does not exist", SOME_ID)); + + underTest.start(); + underTest.getById(SOME_ID); + } + + @Test + public void getById_throws_ISE_of_Metric_is_disabled() { + expectedException.expect(IllegalStateException.class); + expectedException.expectMessage(String.format("Metric with id '%s' does not exist", 3)); + + dbTester.prepareDbUnit(getClass(), "shared.xml"); + + underTest.start(); + underTest.getById(3); + } + + @Test + public void getById_find_enabled_Metrics() { + dbTester.prepareDbUnit(getClass(), "shared.xml"); + + underTest.start(); + assertThat(underTest.getById(1).getKey()).isEqualTo("ncloc"); + assertThat(underTest.getById(2).getKey()).isEqualTo("coverage"); } + } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/metric/MetricRepositoryImplTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/metric/MetricRepositoryImplTest/shared.xml new file mode 100644 index 00000000000..49bb4594af1 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/computation/metric/MetricRepositoryImplTest/shared.xml @@ -0,0 +1,7 @@ + + + + + + + -- 2.39.5