From: Simon Brandhof Date: Wed, 17 Jun 2015 22:03:18 +0000 (+0200) Subject: Refactor registration of metrics at server startup X-Git-Tag: 5.2-RC1~1433 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=021bf45623b748e70f20d956e86d595191241786;p=sonarqube.git Refactor registration of metrics at server startup Replace Hibernate by MyBatis --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java index b4194d66178..481ca005b96 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java @@ -36,6 +36,7 @@ import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.purge.PurgeDao; +import org.sonar.core.qualitygate.db.QualityGateConditionDao; import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.resource.ResourceDao; import org.sonar.core.technicaldebt.db.CharacteristicDao; @@ -107,6 +108,7 @@ public class DbClient { private final EventDao eventDao; private final PurgeDao purgeDao; private final CustomMeasureDao customMeasureDao; + private final QualityGateConditionDao gateConditionDao; public DbClient(Database db, MyBatis myBatis, DaoComponent... daoComponents) { this.db = db; @@ -150,6 +152,7 @@ public class DbClient { componentLinkDao = getDao(map, ComponentLinkDao.class); eventDao = getDao(map, EventDao.class); purgeDao = getDao(map, PurgeDao.class); + gateConditionDao = getDao(map, QualityGateConditionDao.class); } public Database database() { @@ -296,6 +299,10 @@ public class DbClient { return purgeDao; } + public QualityGateConditionDao gateConditionDao() { + return gateConditionDao; + } + private K getDao(Map map, Class clazz) { return (K) map.get(clazz); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/DefaultMetricFinder.java b/server/sonar-server/src/main/java/org/sonar/server/metric/DefaultMetricFinder.java new file mode 100644 index 00000000000..91433ef356f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/DefaultMetricFinder.java @@ -0,0 +1,126 @@ +/* + * 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.metric; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import javax.annotation.Nonnull; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.MetricFinder; +import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; + +import static com.google.common.collect.FluentIterable.from; + +public class DefaultMetricFinder implements MetricFinder { + + private final DbClient dbClient; + + public DefaultMetricFinder(DbClient dbClient) { + this.dbClient = dbClient; + } + + @Override + public Metric findById(int id) { + DbSession session = dbClient.openSession(false); + try { + MetricDto dto = dbClient.metricDao().selectNullableById(session, id); + if (dto != null && dto.isEnabled()) { + return ToMetric.INSTANCE.apply(dto); + } + return null; + } finally { + MyBatis.closeQuietly(session); + } + } + + @Override + public Metric findByKey(String key) { + DbSession session = dbClient.openSession(false); + try { + MetricDto dto = dbClient.metricDao().selectNullableByKey(session, key); + if (dto != null && dto.isEnabled()) { + return ToMetric.INSTANCE.apply(dto); + } + return null; + } finally { + MyBatis.closeQuietly(session); + } + } + + @Override + public Collection findAll(List metricKeys) { + DbSession session = dbClient.openSession(false); + try { + List dtos = dbClient.metricDao().selectNullableByKeys(session, metricKeys); + return from(dtos).filter(IsEnabled.INSTANCE).transform(ToMetric.INSTANCE).toList(); + } finally { + MyBatis.closeQuietly(session); + } + } + + @Override + public Collection findAll() { + DbSession session = dbClient.openSession(false); + try { + List dtos = dbClient.metricDao().selectEnabled(session); + return from(dtos).transform(ToMetric.INSTANCE).toList(); + } finally { + MyBatis.closeQuietly(session); + } + } + + private enum IsEnabled implements Predicate { + INSTANCE; + @Override + public boolean apply(@Nonnull MetricDto dto) { + return dto.isEnabled(); + } + } + + private enum ToMetric implements Function { + INSTANCE; + + @Override + public Metric apply(@Nonnull MetricDto dto) { + Metric metric = new Metric<>(); + metric.setId(dto.getId()); + metric.setKey(dto.getKey()); + metric.setDescription(dto.getDescription()); + metric.setName(dto.getShortName()); + metric.setBestValue(dto.getBestValue()); + metric.setDomain(dto.getDomain()); + metric.setEnabled(dto.isEnabled()); + metric.setDirection(dto.getDirection()); + metric.setHidden(dto.isHidden()); + metric.setQualitative(dto.isQualitative()); + metric.setType(Metric.ValueType.valueOf(dto.getValueType())); + metric.setOptimizedBestValue(dto.isOptimizedBestValue()); + metric.setUserManaged(dto.isUserManaged()); + metric.setWorstValue(dto.getWorstValue()); + return metric; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java b/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java index 8765806f735..0be58572229 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/persistence/MetricDao.java @@ -112,16 +112,20 @@ public class MetricDao implements DaoComponent { return session.getMapper(MetricMapper.class); } - public void disable(final DbSession session, List ids) { + public void disableByIds(final DbSession session, List ids) { DaoUtils.executeLargeInputsWithoutOutput(ids, new Function, Void>() { @Override public Void apply(@Nonnull List input) { - mapper(session).disable(input); + mapper(session).disableByIds(input); return null; } }); } + public void disableByKey(final DbSession session, String key) { + mapper(session).disableByKey(key); + } + public void update(DbSession session, MetricDto metric) { mapper(session).update(metric); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java index d388560ba89..5ef5649bf4e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/metric/ws/DeleteAction.java @@ -74,7 +74,7 @@ public class DeleteAction implements MetricsWsAction { DbSession dbSession = dbClient.openSession(false); try { List ids = loadIds(dbSession, request); - dbClient.metricDao().disable(dbSession, ids); + dbClient.metricDao().disableByIds(dbSession, ids); dbClient.customMeasureDao().deleteByMetricIds(dbSession, ids); dbSession.commit(); } finally { diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 42ef539aa6c..85f0d0495e9 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -37,7 +37,6 @@ import org.sonar.core.issue.IssueFilterSerializer; import org.sonar.core.issue.IssueUpdater; import org.sonar.core.issue.workflow.FunctionExecutor; import org.sonar.core.issue.workflow.IssueWorkflow; -import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.permission.PermissionFacade; import org.sonar.core.qualitygate.db.ProjectQgateAssociationDao; import org.sonar.core.qualitygate.db.QualityGateConditionDao; @@ -46,7 +45,6 @@ import org.sonar.core.resource.DefaultResourceTypes; import org.sonar.core.timemachine.Periods; import org.sonar.core.user.DefaultUserFinder; import org.sonar.core.user.DeprecatedUserFinder; -import org.sonar.jpa.dao.MeasuresDao; import org.sonar.server.activity.ActivityService; import org.sonar.server.activity.RubyQProfileActivityService; import org.sonar.server.activity.index.ActivityIndex; @@ -170,6 +168,7 @@ import org.sonar.server.measure.template.ProjectFilter; import org.sonar.server.measure.ws.ManualMeasuresWs; import org.sonar.server.measure.ws.TimeMachineWs; import org.sonar.server.metric.CoreCustomMetrics; +import org.sonar.server.metric.DefaultMetricFinder; import org.sonar.server.metric.ws.MetricsWsModule; import org.sonar.server.notification.DefaultNotificationManager; import org.sonar.server.notification.NotificationCenter; @@ -486,8 +485,6 @@ public class PlatformLevel4 extends PlatformLevel { ActivityMapping.class, // measure - MeasuresDao.class, - MeasureFilterFactory.class, MeasureFilterExecutor.class, MeasureFilterEngine.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterMetrics.java b/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterMetrics.java index 23636e51133..1000c1078e6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterMetrics.java +++ b/server/sonar-server/src/main/java/org/sonar/server/startup/RegisterMetrics.java @@ -20,99 +20,142 @@ package org.sonar.server.startup; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; +import com.google.common.base.Function; import com.google.common.collect.Maps; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metrics; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; -import org.sonar.core.qualitygate.db.QualityGateConditionDao; -import org.sonar.jpa.dao.MeasuresDao; - -import java.util.List; -import java.util.Map; +import org.sonar.core.metric.db.MetricDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; +import static com.google.common.collect.FluentIterable.from; +import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Lists.newArrayList; public class RegisterMetrics { private static final Logger LOG = Loggers.get(RegisterMetrics.class); - private final MeasuresDao measuresDao; + private final DbClient dbClient; private final Metrics[] metricsRepositories; - private final QualityGateConditionDao conditionDao; - public RegisterMetrics(MeasuresDao measuresDao, QualityGateConditionDao conditionDao, Metrics[] metricsRepositories) { - this.measuresDao = measuresDao; + public RegisterMetrics(DbClient dbClient, Metrics[] metricsRepositories) { + this.dbClient = dbClient; this.metricsRepositories = metricsRepositories; - this.conditionDao = conditionDao; } /** * Used when no plugin is defining Metrics */ - public RegisterMetrics(MeasuresDao measuresDao, QualityGateConditionDao conditionDao) { - this(measuresDao, conditionDao, new Metrics[]{}); + public RegisterMetrics(DbClient dbClient) { + this(dbClient, new Metrics[] {}); } public void start() { - Profiler profiler = Profiler.create(LOG).startInfo("Register metrics"); - measuresDao.disableAutomaticMetrics(); + register(concat(CoreMetrics.getMetrics(), getPluginMetrics())); + } - List metricsToRegister = newArrayList(); - metricsToRegister.addAll(CoreMetrics.getMetrics()); - metricsToRegister.addAll(getMetricsRepositories()); - register(metricsToRegister); - cleanAlerts(); + void register(Iterable metrics) { + Profiler profiler = Profiler.create(LOG).startInfo("Register metrics"); + DbSession session = dbClient.openSession(false); + try { + save(session, metrics); + sanitizeQualityGates(session); + session.commit(); + } finally { + MyBatis.closeQuietly(session); + } profiler.stopDebug(); } + private void sanitizeQualityGates(DbSession session) { + dbClient.gateConditionDao().deleteConditionsWithInvalidMetrics(session); + } + + private void save(DbSession session, Iterable metrics) { + Map basesByKey = new HashMap<>(); + for (MetricDto base : from(dbClient.metricDao().selectEnabled(session)).toList()) { + basesByKey.put(base.getKey(), base); + } + + for (Metric metric : metrics) { + MetricDto dto = MetricToDto.INSTANCE.apply(metric); + MetricDto base = basesByKey.get(metric.getKey()); + if (base == null) { + // new metric, never installed + dbClient.metricDao().insert(session, dto); + } else if (!base.isUserManaged()) { + // existing metric, update changes. Existing custom metrics are kept without applying changes. + dto.setId(base.getId()); + dbClient.metricDao().update(session, dto); + } + basesByKey.remove(metric.getKey()); + } + + for (MetricDto nonUpdatedBase : basesByKey.values()) { + if (!nonUpdatedBase.isUserManaged()) { + LOG.info("Disable metric {} [{}]", nonUpdatedBase.getShortName(), nonUpdatedBase.getKey()); + dbClient.metricDao().disableByKey(session, nonUpdatedBase.getKey()); + } + } + } + @VisibleForTesting - List getMetricsRepositories() { + List getPluginMetrics() { List metricsToRegister = newArrayList(); Map metricsByRepository = Maps.newHashMap(); - for (Metrics metrics : metricsRepositories) { checkMetrics(metricsByRepository, metrics); - metricsToRegister.addAll(removeExistingUserManagedMetrics(metrics.getMetrics())); + metricsToRegister.addAll(metrics.getMetrics()); } return metricsToRegister; } - private List removeExistingUserManagedMetrics(List metrics) { - return newArrayList(Iterables.filter(metrics, new Predicate() { - @Override - public boolean apply(Metric metric) { - // It should be better to use the template mechanism (as it's done in #RegisterDashboards to register provided user manager metrics - return !metric.getUserManaged() || measuresDao.getMetric(metric.getKey()) == null; - } - })); - } - private void checkMetrics(Map metricsByRepository, Metrics metrics) { for (Metric metric : metrics.getMetrics()) { String metricKey = metric.getKey(); if (CoreMetrics.getMetrics().contains(metric)) { - throw new IllegalStateException("The following metric is already defined in sonar: " + metricKey); + throw new IllegalStateException(String.format("Metric [%s] is already defined by SonarQube", metricKey)); } Metrics anotherRepository = metricsByRepository.get(metricKey); if (anotherRepository != null) { - throw new IllegalStateException("The metric '" + metricKey + "' is already defined in the extension: " + anotherRepository); + throw new IllegalStateException(String.format("Metric [%s] is already defined by the repository [%s]", metricKey, anotherRepository)); } metricsByRepository.put(metricKey, metrics); } } - protected void cleanAlerts() { - LOG.info("Cleaning quality gate conditions"); - conditionDao.deleteConditionsWithInvalidMetrics(); - } - - protected void register(List metrics) { - measuresDao.registerMetrics(metrics); + private enum MetricToDto implements Function { + INSTANCE; + @Override + @Nonnull + public MetricDto apply(@Nonnull Metric metric) { + MetricDto dto = new MetricDto(); + dto.setId(metric.getId()); + dto.setKey(metric.getKey()); + dto.setDescription(metric.getDescription()); + dto.setShortName(metric.getName()); + dto.setBestValue(metric.getBestValue()); + dto.setDomain(metric.getDomain()); + dto.setEnabled(metric.getEnabled()); + dto.setDirection(metric.getDirection()); + dto.setHidden(metric.isHidden()); + dto.setQualitative(metric.getQualitative()); + dto.setValueType(metric.getType().name()); + dto.setOptimizedBestValue(metric.isOptimizedBestValue()); + dto.setUserManaged(metric.getUserManaged()); + dto.setWorstValue(metric.getWorstValue()); + return dto; + } } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/metric/DefaultMetricFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/metric/DefaultMetricFinderTest.java new file mode 100644 index 00000000000..3d257d40b1a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/metric/DefaultMetricFinderTest.java @@ -0,0 +1,71 @@ +/* + * 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.metric; + +import java.util.Arrays; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.sonar.core.persistence.DbTester; +import org.sonar.server.db.DbClient; +import org.sonar.server.metric.persistence.MetricDao; +import org.sonar.test.DbTests; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.Assert.assertThat; + +@Category(DbTests.class) +public class DefaultMetricFinderTest { + + @ClassRule + public static DbTester dbTester = new DbTester(); + + DefaultMetricFinder finder; + + @Before + public void setUp() { + dbTester.prepareDbUnit(DefaultMetricFinderTest.class, "shared.xml"); + finder = new DefaultMetricFinder(new DbClient(dbTester.database(), dbTester.myBatis(), new MetricDao())); + } + + @Test + public void shouldFindAll() { + assertThat(finder.findAll().size(), is(2)); + } + + @Test + public void shouldFindByKeys() { + assertThat(finder.findAll(Arrays.asList("ncloc", "foo", "coverage")).size(), is(2)); + } + + @Test + public void shouldFindById() { + assertThat(finder.findById(1).getKey(), is("ncloc")); + assertThat(finder.findById(3), nullValue()); + } + + @Test + public void shouldFindByKey() { + assertThat(finder.findByKey("ncloc").getKey(), is("ncloc")); + assertThat(finder.findByKey("disabled"), nullValue()); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java b/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java index af536fab964..c37600379b9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/startup/RegisterMetricsTest.java @@ -19,120 +19,127 @@ */ package org.sonar.server.startup; -import com.google.common.collect.Lists; +import java.util.Collections; +import java.util.List; +import org.junit.ClassRule; import org.junit.Test; +import org.junit.experimental.categories.Category; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metrics; +import org.sonar.core.persistence.DbTester; import org.sonar.core.qualitygate.db.QualityGateConditionDao; -import org.sonar.jpa.dao.MeasuresDao; -import org.sonar.jpa.test.AbstractDbUnitTestCase; - -import java.util.Arrays; -import java.util.List; +import org.sonar.server.db.DbClient; +import org.sonar.server.metric.persistence.MetricDao; +import org.sonar.test.DbTests; +import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -public class RegisterMetricsTest extends AbstractDbUnitTestCase { +@Category(DbTests.class) +public class RegisterMetricsTest { + + @ClassRule + public static DbTester dbTester = new DbTester(); + /** + * Insert new metrics, including custom metrics + */ @Test - public void shouldSaveIfNew() { - setupData("shouldSaveIfNew"); + public void insert_new_metrics() { + dbTester.prepareDbUnit(getClass(), "insert_new_metrics.xml"); - Metric metric1 = new Metric.Builder("new1", "short1", Metric.ValueType.FLOAT) + Metric m1 = new Metric.Builder("m1", "One", Metric.ValueType.FLOAT) .setDescription("desc1") .setDirection(1) .setQualitative(true) .setDomain("domain1") .setUserManaged(false) .create(); - Metric metric2 = new Metric.Builder("new2", "short2", Metric.ValueType.FLOAT) - .setDescription("desc2") - .setDirection(1) - .setQualitative(true) - .setDomain("domain2") - .setUserManaged(false) + Metric custom = new Metric.Builder("custom", "Custom", Metric.ValueType.FLOAT) + .setDescription("This is a custom metric") + .setUserManaged(true) .create(); - RegisterMetrics synchronizer = new RegisterMetrics(new MeasuresDao(getSession()), mock(QualityGateConditionDao.class), new Metrics[0]); - synchronizer.register(Arrays.asList(metric1, metric2)); - checkTables("shouldSaveIfNew", "metrics"); + RegisterMetrics register = new RegisterMetrics(dbClient()); + register.register(asList(m1, custom)); + dbTester.assertDbUnit(getClass(), "insert_new_metrics-result.xml", "metrics"); } + /** + * Update existing metrics, except if custom metric + */ @Test - public void shouldUpdateIfAlreadyExists() { - setupData("shouldUpdateIfAlreadyExists"); + public void update_non_custom_metrics() { + dbTester.prepareDbUnit(getClass(), "update_non_custom_metrics.xml"); - RegisterMetrics synchronizer = new RegisterMetrics(new MeasuresDao(getSession()), mock(QualityGateConditionDao.class), new Metrics[0]); - synchronizer.register(Lists.newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) + RegisterMetrics register = new RegisterMetrics(dbClient()); + Metric m1 = new Metric.Builder("m1", "New name", Metric.ValueType.FLOAT) .setDescription("new description") .setDirection(-1) .setQualitative(true) .setDomain("new domain") .setUserManaged(false) - .create())); + .setHidden(true) + .create(); + Metric custom = new Metric.Builder("custom", "New custom", Metric.ValueType.FLOAT) + .setDescription("New description of custom metric") + .setUserManaged(true) + .create(); + register.register(asList(m1, custom)); - checkTables("shouldUpdateIfAlreadyExists", "metrics"); + dbTester.assertDbUnit(getClass(), "update_non_custom_metrics-result.xml", "metrics"); } @Test - public void shouldAddUserManagesMetric() { - Metrics metrics = mock(Metrics.class); - when(metrics.getMetrics()).thenReturn(Lists.newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) - .setDescription("new description") - .setDirection(-1) - .setQualitative(true) - .setDomain("new domain") - .setUserManaged(true) - .create())); + public void disable_undefined_metrics() { + dbTester.prepareDbUnit(getClass(), "disable_undefined_metrics.xml"); - MeasuresDao measuresDao = new MeasuresDao(getSession()); - RegisterMetrics loader = new RegisterMetrics(measuresDao, mock(QualityGateConditionDao.class), new Metrics[] {metrics}); - List result = loader.getMetricsRepositories(); + RegisterMetrics register = new RegisterMetrics(dbClient()); + register.register(Collections.emptyList()); - assertThat(result).hasSize(1); + dbTester.assertDbUnit(getClass(), "disable_undefined_metrics-result.xml", "metrics"); } @Test - public void shouldNotUpdateUserManagesMetricIfAlreadyExists() { - setupData("shouldNotUpdateUserManagesMetricIfAlreadyExists"); + public void insert_core_metrics() { + dbTester.truncateTables(); - Metrics metrics = mock(Metrics.class); - when(metrics.getMetrics()).thenReturn(Lists.newArrayList(new Metric.Builder("key", "new short name", Metric.ValueType.FLOAT) - .setDescription("new description") - .setDirection(-1) - .setQualitative(true) - .setDomain("new domain") - .setUserManaged(true) - .create())); + RegisterMetrics register = new RegisterMetrics(dbClient()); + register.start(); - MeasuresDao measuresDao = new MeasuresDao(getSession()); - RegisterMetrics loader = new RegisterMetrics(measuresDao, mock(QualityGateConditionDao.class), new Metrics[] {metrics}); - List result = loader.getMetricsRepositories(); + assertThat(dbTester.countRowsOfTable("metrics")).isEqualTo(CoreMetrics.getMetrics().size()); + } + + @Test(expected = IllegalStateException.class) + public void fail_if_duplicated_plugin_metrics() throws Exception { + Metrics plugin1 = new TestMetrics(new Metric.Builder("m1", "In first plugin", Metric.ValueType.FLOAT).create()); + Metrics plugin2 = new TestMetrics(new Metric.Builder("m1", "In second plugin", Metric.ValueType.FLOAT).create()); - assertThat(result).isEmpty(); + new RegisterMetrics(dbClient(), new Metrics[]{plugin1, plugin2}).start(); } - @Test - public void shouldEnableOnlyLoadedMetrics() { - setupData("shouldEnableOnlyLoadedMetrics"); + @Test(expected = IllegalStateException.class) + public void fail_if_plugin_duplicates_core_metric() throws Exception { + Metrics plugin = new TestMetrics(new Metric.Builder("ncloc", "In plugin", Metric.ValueType.FLOAT).create()); - MeasuresDao measuresDao = new MeasuresDao(getSession()); - RegisterMetrics loader = new RegisterMetrics(measuresDao, mock(QualityGateConditionDao.class), new Metrics[0]); - loader.start(); + new RegisterMetrics(dbClient(), new Metrics[]{plugin}).start(); + } - assertThat(measuresDao.getMetric("deprecated").getEnabled()).isFalse(); - assertThat(measuresDao.getMetric(CoreMetrics.COMPLEXITY_KEY).getEnabled()).isTrue(); + private DbClient dbClient() { + return new DbClient(dbTester.database(), dbTester.myBatis(), new MetricDao(), new QualityGateConditionDao(dbTester.myBatis())); } - @Test - public void clean_quality_gate_conditions() { - QualityGateConditionDao conditionDao = mock(QualityGateConditionDao.class); - RegisterMetrics loader = new RegisterMetrics(new MeasuresDao(getSession()), conditionDao, new Metrics[0]); - loader.cleanAlerts(); - verify(conditionDao).deleteConditionsWithInvalidMetrics(); + private class TestMetrics implements Metrics { + private final List metrics; + + public TestMetrics(Metric... metrics) { + this.metrics = asList(metrics); + } + + @Override + public List getMetrics() { + return metrics; + } } } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/metric/DefaultMetricFinderTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/metric/DefaultMetricFinderTest/shared.xml new file mode 100644 index 00000000000..dd645d66ec1 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/metric/DefaultMetricFinderTest/shared.xml @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/disable_undefined_metrics-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/disable_undefined_metrics-result.xml new file mode 100644 index 00000000000..dbedde99411 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/disable_undefined_metrics-result.xml @@ -0,0 +1,8 @@ + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/disable_undefined_metrics.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/disable_undefined_metrics.xml new file mode 100644 index 00000000000..b48ad61cd33 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/disable_undefined_metrics.xml @@ -0,0 +1,9 @@ + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/insert_new_metrics-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/insert_new_metrics-result.xml new file mode 100644 index 00000000000..1bb5088d8e0 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/insert_new_metrics-result.xml @@ -0,0 +1,11 @@ + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/insert_new_metrics.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/insert_new_metrics.xml new file mode 100644 index 00000000000..a1c54e4625a --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/insert_new_metrics.xml @@ -0,0 +1,4 @@ + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldAddUserManagesMetric-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldAddUserManagesMetric-result.xml deleted file mode 100644 index e00703108a3..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldAddUserManagesMetric-result.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldAddUserManagesMetric.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldAddUserManagesMetric.xml deleted file mode 100644 index f1e73ac59ed..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldAddUserManagesMetric.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldEnableOnlyLoadedMetrics.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldEnableOnlyLoadedMetrics.xml deleted file mode 100644 index 93aa067888f..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldEnableOnlyLoadedMetrics.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldNotUpdateUserManagesMetricIfAlreadyExists-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldNotUpdateUserManagesMetricIfAlreadyExists-result.xml deleted file mode 100644 index 968b9e9f3d8..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldNotUpdateUserManagesMetricIfAlreadyExists-result.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldNotUpdateUserManagesMetricIfAlreadyExists.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldNotUpdateUserManagesMetricIfAlreadyExists.xml deleted file mode 100644 index ed24c0d990a..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldNotUpdateUserManagesMetricIfAlreadyExists.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldSaveIfNew-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldSaveIfNew-result.xml deleted file mode 100644 index 182a4a8e3bb..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldSaveIfNew-result.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldSaveIfNew.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldSaveIfNew.xml deleted file mode 100644 index 3f7c90205e1..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldSaveIfNew.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldUpdateIfAlreadyExists-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldUpdateIfAlreadyExists-result.xml deleted file mode 100644 index ed9a86d0f2a..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldUpdateIfAlreadyExists-result.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldUpdateIfAlreadyExists.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldUpdateIfAlreadyExists.xml deleted file mode 100644 index fb9242edb48..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/shouldUpdateIfAlreadyExists.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/update_non_custom_metrics-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/update_non_custom_metrics-result.xml new file mode 100644 index 00000000000..886e1d2d8f4 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/update_non_custom_metrics-result.xml @@ -0,0 +1,15 @@ + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/update_non_custom_metrics.xml b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/update_non_custom_metrics.xml new file mode 100644 index 00000000000..483a9cb43c6 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/startup/RegisterMetricsTest/update_non_custom_metrics.xml @@ -0,0 +1,12 @@ + + + diff --git a/sonar-core/src/main/java/org/sonar/core/metric/DefaultMetricFinder.java b/sonar-core/src/main/java/org/sonar/core/metric/DefaultMetricFinder.java deleted file mode 100644 index 087a6be91b8..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/metric/DefaultMetricFinder.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * SonarQube is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.core.metric; - -import com.google.common.collect.Lists; -import org.sonar.api.measures.Metric; -import org.sonar.api.measures.MetricFinder; -import org.sonar.jpa.session.DatabaseSessionFactory; - -import java.util.Collection; -import java.util.List; - -public class DefaultMetricFinder implements MetricFinder { - - private static final String ENABLED = "enabled"; - private DatabaseSessionFactory sessionFactory; - - public DefaultMetricFinder(DatabaseSessionFactory sessionFactory) { - this.sessionFactory = sessionFactory; - } - - @Override - public Metric findById(int id) { - return sessionFactory.getSession().getSingleResult(Metric.class, "id", id, ENABLED, true); - } - - @Override - public Metric findByKey(String key) { - return sessionFactory.getSession().getSingleResult(Metric.class, "key", key, ENABLED, true); - } - - @Override - public Collection findAll(List metricKeys) { - List result = Lists.newLinkedList(); - for (String metricKey : metricKeys) { - Metric metric = findByKey(metricKey); - if (metric != null) { - result.add(metric); - } - } - return result; - } - - @Override - public Collection findAll() { - return doFindAll(); - } - - protected Collection doFindAll() { - return sessionFactory.getSession().getResults(Metric.class, ENABLED, true); - } - -} diff --git a/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java b/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java index dd216603d05..280fb82f4e0 100644 --- a/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/metric/db/MetricMapper.java @@ -40,7 +40,9 @@ public interface MetricMapper { List selectByKeys(@Param("keys") List keys); - void disable(@Param("ids") List ids); + void disableByIds(@Param("ids") List ids); + + void disableByKey(@Param("key") String key); int countEnabled(@Param("isCustom") @Nullable Boolean isCustom); diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java index 03372a70717..17764552b20 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/DaoUtils.java @@ -39,6 +39,7 @@ import org.sonar.core.permission.PermissionDao; import org.sonar.core.permission.PermissionTemplateDao; import org.sonar.core.properties.PropertiesDao; import org.sonar.core.purge.PurgeDao; +import org.sonar.core.qualitygate.db.QualityGateConditionDao; import org.sonar.core.qualityprofile.db.ActiveRuleDao; import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.resource.ResourceDao; @@ -84,6 +85,7 @@ public final class DaoUtils { PermissionDao.class, PermissionTemplateDao.class, PropertiesDao.class, + QualityGateConditionDao.class, QualityProfileDao.class, PurgeDao.class, CharacteristicDao.class, diff --git a/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDao.java b/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDao.java index a634f15bf61..5c1eec7664f 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDao.java +++ b/sonar-core/src/main/java/org/sonar/core/qualitygate/db/QualityGateConditionDao.java @@ -20,6 +20,7 @@ package org.sonar.core.qualitygate.db; import org.apache.ibatis.session.SqlSession; +import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.MyBatis; import java.util.Collection; @@ -28,7 +29,7 @@ import java.util.Date; /** * @since 4.3 */ -public class QualityGateConditionDao { +public class QualityGateConditionDao implements DaoComponent { private final MyBatis myBatis; diff --git a/sonar-core/src/main/java/org/sonar/jpa/dao/MeasuresDao.java b/sonar-core/src/main/java/org/sonar/jpa/dao/MeasuresDao.java deleted file mode 100644 index 372674b94c4..00000000000 --- a/sonar-core/src/main/java/org/sonar/jpa/dao/MeasuresDao.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.jpa.dao; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.Predicate; -import org.sonar.api.database.DatabaseSession; -import org.sonar.api.measures.Metric; - -public class MeasuresDao { - - private final DatabaseSession session; - private final Map metricsByName = new HashMap<>(); - - public MeasuresDao(DatabaseSession session) { - this.session = session; - } - - public Metric getMetric(String metricName) { - return getMetricsByName().get(metricName); - } - - public Collection getMetrics() { - return getMetricsByName().values(); - } - - public Collection getEnabledMetrics() { - return CollectionUtils.select(getMetricsByName().values(), new Predicate() { - @Override - public boolean evaluate(Object o) { - return ((Metric) o).getEnabled(); - } - }); - } - - public void disableAutomaticMetrics() { - session.createQuery("update " + Metric.class.getSimpleName() + " m set m.enabled=false where m.userManaged=false").executeUpdate(); - session.commit(); - metricsByName.clear(); - } - - public void registerMetrics(Collection metrics) { - if (metrics != null) { - for (Metric metric : metrics) { - metric.setEnabled(Boolean.TRUE); - persistMetricWithoutClear(metric); - } - session.commit(); - } - metricsByName.clear(); - } - - private void persistMetricWithoutClear(Metric metric) { - Metric dbMetric = getMetric(metric.getKey()); - if (dbMetric != null) { - dbMetric.merge(metric); - session.getEntityManager().merge(dbMetric); - - } else { - session.getEntityManager().persist(new Metric().merge(metric)); - } - } - - private Map getMetricsByName() { - if (metricsByName.isEmpty()) { - List metrics = session.getResults(Metric.class); - for (Metric metric : metrics) { - metricsByName.put(metric.getKey(), metric); - } - } - return metricsByName; - } - -} diff --git a/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml b/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml index 9278b7252a5..a91e4c49259 100644 --- a/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/metric/db/MetricMapper.xml @@ -47,6 +47,7 @@ ORDER BY UPPER(m.short_name), m.short_name + - + update metrics set enabled=${_false} @@ -107,6 +111,12 @@ + + update metrics + set enabled=${_false} + where name=#{key} + +