From 56554cc77e5f0eba7fc75e7d6b340cdc7c2e4698 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Mon, 23 Jun 2014 14:52:56 +0200 Subject: [PATCH] SONAR-5216 compare quality profile dates when batch detects profile changes --- pom.xml | 12 ++++ sonar-batch/pom.xml | 5 ++ .../mediumtest/AnalyzerMediumTester.java | 13 ++-- .../sonar/batch/rule/ActiveRulesProvider.java | 4 +- .../org/sonar/batch/rule/ModuleQProfiles.java | 2 +- .../java/org/sonar/batch/rule/QProfile.java | 38 +++++++++--- .../batch/rule/QProfileEventsDecorator.java | 32 +++++----- .../sonar/batch/rule/QProfileVerifier.java | 4 +- .../batch/rule/RulesProfileProvider.java | 6 +- .../org/sonar/batch/rule/UsedQProfiles.java | 29 +++++---- .../rules/DefaultQProfileReferential.java | 8 ++- .../batch/rule/ActiveRulesProviderTest.java | 4 +- .../sonar/batch/rule/ModuleQProfilesTest.java | 31 +++++----- .../batch/rule/QProfileDecoratorTest.java | 6 +- .../rule/QProfileEventsDecoratorTest.java | 44 +++++++++----- .../sonar/batch/rule/QProfileSensorTest.java | 26 +++++--- .../batch/rule/QProfileVerifierTest.java | 4 +- .../batch/rule/RulesProfileProviderTest.java | 4 +- .../sonar/batch/rule/UsedQProfilesTest.java | 24 +++++--- .../batch/rule/ModuleQProfilesTest/shared.xml | 16 +++-- sonar-core/pom.xml | 7 --- .../java/org/sonar/core/UtcDateUtils.java | 54 +++++++++++++++++ .../persistence/dialect/DialectUtils.java | 5 ++ .../migration/v44/Migration44Mapper.java | 22 +++++-- .../migration/v44/QProfileDto44.java | 60 +++++++++++++++++++ .../migration/v44/package-info.java | 24 ++++++++ .../qualityprofile/db/QualityProfileDao.java | 10 +++- .../qualityprofile/db/QualityProfileDto.java | 22 ++++++- .../migration/v44/Migration44Mapper.xml | 8 ++- .../org/sonar/core/persistence/schema-h2.ddl | 2 +- .../db/QualityProfileMapper.xml | 13 ++-- .../java/org/sonar/core/UtcDateUtilsTest.java | 46 ++++++++++++++ .../db/QualityProfileDaoTest.java | 12 +++- .../v44/ConvertProfileMeasures.java | 24 ++++---- .../v44/FeedQProfileDatesMigration.java | 9 +-- .../qualityprofile/QProfileFactory.java | 7 ++- .../org/sonar/server/db/DbClientTest.java | 3 +- .../sonar/server/rule/RegisterRulesTest.java | 4 +- 38 files changed, 492 insertions(+), 152 deletions(-) create mode 100644 sonar-core/src/main/java/org/sonar/core/UtcDateUtils.java create mode 100644 sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/QProfileDto44.java create mode 100644 sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/package-info.java create mode 100644 sonar-core/src/test/java/org/sonar/core/UtcDateUtilsTest.java diff --git a/pom.xml b/pom.xml index 71612ee7c9a..e94e6fb3e05 100644 --- a/pom.xml +++ b/pom.xml @@ -916,6 +916,18 @@ + + com.google.code.bean-matchers + bean-matchers + 0.9 + test + + + org.hamcrest + hamcrest-core + + + org.easytesting fest-assert diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index dbd2cc91a69..f09d5e5b132 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -120,6 +120,11 @@ mockito-core test + + com.google.code.bean-matchers + bean-matchers + test + org.codehaus.sonar sonar-testing-harness diff --git a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java index 0f2a941412c..c597e96d29c 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java +++ b/sonar-batch/src/main/java/org/sonar/batch/mediumtest/AnalyzerMediumTester.java @@ -30,7 +30,6 @@ import org.sonar.api.batch.analyzer.measure.AnalyzerMeasure; import org.sonar.api.batch.debt.internal.DefaultDebtModel; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.RulesBuilder; -import org.sonar.batch.rule.QProfile; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.measures.MetricFinder; @@ -44,6 +43,7 @@ import org.sonar.batch.bootstrapper.Batch; import org.sonar.batch.bootstrapper.EnvironmentInformation; import org.sonar.batch.languages.Language; import org.sonar.batch.languages.LanguagesReferential; +import org.sonar.batch.rule.QProfile; import org.sonar.batch.rules.QProfilesReferential; import org.sonar.batch.scan2.AnalyzerIssueCache; import org.sonar.batch.scan2.AnalyzerMeasureCache; @@ -58,6 +58,7 @@ import java.io.FileReader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -111,12 +112,12 @@ public class AnalyzerMediumTester { } public AnalyzerMediumTesterBuilder addQProfile(String language, String name) { - qProfileReferential.add(new QProfile("TODO", name, language)); + qProfileReferential.add(new QProfile().setKey(name).setName(name).setLanguage(language).setRulesUpdatedAt(new Date())); return this; } public AnalyzerMediumTesterBuilder addDefaultQProfile(String language, String name) { - qProfileReferential.add(new QProfile("TODO", name, language)); + addQProfile(language, name); settingsReferential.globalSettings().put("sonar.profile." + language, name); return this; } @@ -381,10 +382,10 @@ public class AnalyzerMediumTester { } public void add(QProfile qprofile) { - if (!profiles.containsKey(qprofile.language())) { - profiles.put(qprofile.language(), new HashMap()); + if (!profiles.containsKey(qprofile.getLanguage())) { + profiles.put(qprofile.getLanguage(), new HashMap()); } - profiles.get(qprofile.language()).put(qprofile.name(), qprofile); + profiles.get(qprofile.getLanguage()).put(qprofile.getName(), qprofile); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java index 06abd2883a0..a4fec80b1f5 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ActiveRulesProvider.java @@ -51,11 +51,11 @@ public class ActiveRulesProvider extends ProviderAdapter { ActiveRulesBuilder builder = new ActiveRulesBuilder(); for (QProfile qProfile : qProfiles.findAll()) { ListMultimap paramDtosByActiveRuleId = ArrayListMultimap.create(); - for (ActiveRuleParamDto dto : dao.selectParamsByProfileKey(qProfile.key())) { + for (ActiveRuleParamDto dto : dao.selectParamsByProfileKey(qProfile.getKey())) { paramDtosByActiveRuleId.put(dto.getActiveRuleId(), dto); } - for (ActiveRuleDto activeDto : dao.selectByProfileKey(qProfile.key())) { + for (ActiveRuleDto activeDto : dao.selectByProfileKey(qProfile.getKey())) { Rule rule = ruleFinder.findById(activeDto.getRulId()); if (rule != null) { NewActiveRule newActiveRule = builder.create(rule.ruleKey()); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java index e9ba2db4352..9bc04bd9c8a 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/ModuleQProfiles.java @@ -56,7 +56,7 @@ public class ModuleQProfiles implements BatchComponent { profile = loadQProfile(qProfileRef, settings, language.key()); } if (profile != null) { - builder.put(profile.language(), profile); + builder.put(profile.getLanguage(), profile); } } byLanguage = builder.build(); diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfile.java b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfile.java index c00fa54a23e..73bca934108 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfile.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfile.java @@ -21,26 +21,47 @@ package org.sonar.batch.rule; import com.google.common.base.Objects; +import java.util.Date; + public class QProfile { - private final String key, name, language; + private String key, name, language; + private Date rulesUpdatedAt; + + public String getKey() { + return key; + } - public QProfile(String key, String name, String language) { + public QProfile setKey(String key) { this.key = key; - this.name = name; - this.language = language; + return this; } - public String name() { + public String getName() { return name; } - public String language() { + public QProfile setName(String name) { + this.name = name; + return this; + } + + public String getLanguage() { return language; } - public String key() { - return key; + public QProfile setLanguage(String language) { + this.language = language; + return this; + } + + public Date getRulesUpdatedAt() { + return rulesUpdatedAt; + } + + public QProfile setRulesUpdatedAt(Date d) { + this.rulesUpdatedAt = d; + return this; } @Override @@ -67,6 +88,7 @@ public class QProfile { .add("key", key) .add("name", name) .add("language", language) + .add("rulesUpdatedAt", rulesUpdatedAt) .toString(); } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java index 66fc512d7c1..68aa7d907cd 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileEventsDecorator.java @@ -78,36 +78,34 @@ public class QProfileEventsDecorator implements Decorator { // Detect new profiles or updated profiles for (QProfile profile : currentProfiles.values()) { - QProfile previousProfile = previousProfiles.get(profile.key()); + QProfile previousProfile = previousProfiles.get(profile.getKey()); if (previousProfile != null) { - // TODO compare date + if (profile.getRulesUpdatedAt().after(previousProfile.getRulesUpdatedAt())) { + markAsUsed(context, profile); + } } else { - usedProfile(context, profile); + markAsUsed(context, profile); } } // Detect profiles that are not used anymore for (QProfile previousProfile : previousProfiles.values()) { - if (!currentProfiles.containsKey(previousProfile.key())) { - stopUsedProfile(context, previousProfile); + if (!currentProfiles.containsKey(previousProfile.getKey())) { + markAsUnused(context, previousProfile); } } } - private void stopUsedProfile(DecoratorContext context, QProfile profile) { - Language language = languages.get(profile.language()); - String languageName = language != null ? language.getName() : profile.language(); - context.createEvent("Stop using " + format(profile) + " (" + languageName + ")", format(profile) + " no more used for " + languageName, Event.CATEGORY_PROFILE, null); + private void markAsUnused(DecoratorContext context, QProfile profile) { + Language language = languages.get(profile.getLanguage()); + String languageName = language != null ? language.getName() : profile.getLanguage(); + context.createEvent("Stop using " + profile.getName() + " (" + languageName + ")", profile.getName() + " no more used for " + languageName, Event.CATEGORY_PROFILE, null); } - private void usedProfile(DecoratorContext context, QProfile profile) { - Language language = languages.get(profile.language()); - String languageName = language != null ? language.getName() : profile.language(); - context.createEvent("Use " + format(profile) + " (" + languageName + ")", format(profile) + " used for " + languageName, Event.CATEGORY_PROFILE, null); - } - - private String format(QProfile profile) { - return profile.name(); + private void markAsUsed(DecoratorContext context, QProfile profile) { + Language language = languages.get(profile.getLanguage()); + String languageName = language != null ? language.getName() : profile.getLanguage(); + context.createEvent("Use " + profile.getName() + " (" + languageName + ")", profile.getName() + " used for " + languageName, Event.CATEGORY_PROFILE, null); } @CheckForNull diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java index 7f6db7b10f2..009b9d56253 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/QProfileVerifier.java @@ -55,8 +55,8 @@ public class QProfileVerifier implements BatchComponent { if (profile == null) { logger.warn("No Quality profile found for language " + lang); } else { - logger.info("Quality profile for {}: {}", lang, profile.name()); - if (StringUtils.isNotBlank(defaultName) && defaultName.equals(profile.name())) { + logger.info("Quality profile for {}: {}", lang, profile.getName()); + if (StringUtils.isNotBlank(defaultName) && defaultName.equals(profile.getName())) { defaultNameUsed = true; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java index 96798424939..545bacaa005 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/RulesProfileProvider.java @@ -74,9 +74,9 @@ public class RulesProfileProvider extends ProviderAdapter { private RulesProfile select(QProfile qProfile, ActiveRules activeRules, RuleFinder ruleFinder) { RulesProfile deprecatedProfile = new RulesProfile(); // TODO deprecatedProfile.setVersion(qProfile.version()); - deprecatedProfile.setName(qProfile.name()); - deprecatedProfile.setLanguage(qProfile.language()); - for (org.sonar.api.batch.rule.ActiveRule activeRule : activeRules.findByLanguage(qProfile.language())) { + deprecatedProfile.setName(qProfile.getName()); + deprecatedProfile.setLanguage(qProfile.getLanguage()); + for (org.sonar.api.batch.rule.ActiveRule activeRule : activeRules.findByLanguage(qProfile.getLanguage())) { Rule rule = ruleFinder.findByKey(activeRule.ruleKey()); ActiveRule deprecatedActiveRule = deprecatedProfile.activateRule(rule, RulePriority.valueOf(activeRule.severity())); for (Map.Entry param : activeRule.params().entrySet()) { diff --git a/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java b/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java index 0231ebe5e4c..ac923055950 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rule/UsedQProfiles.java @@ -25,6 +25,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.sonar.api.utils.text.JsonWriter; +import org.sonar.core.UtcDateUtils; import javax.annotation.concurrent.Immutable; @@ -41,9 +42,9 @@ public class UsedQProfiles { private final SortedSet profiles = Sets.newTreeSet(new Comparator() { @Override public int compare(QProfile o1, QProfile o2) { - int c = o1.language().compareTo(o2.language()); + int c = o1.getLanguage().compareTo(o2.getLanguage()); if (c == 0) { - c = o1.name().compareTo(o2.name()); + c = o1.getName().compareTo(o2.getName()); } return c; } @@ -51,10 +52,15 @@ public class UsedQProfiles { public static UsedQProfiles fromJson(String json) { UsedQProfiles result = new UsedQProfiles(); - JsonArray root = new JsonParser().parse(json).getAsJsonArray(); - for (JsonElement elt : root) { - JsonObject profile = elt.getAsJsonObject(); - result.add(new QProfile(profile.get("key").getAsString(), profile.get("name").getAsString(), profile.get("language").getAsString())); + JsonArray jsonRoot = new JsonParser().parse(json).getAsJsonArray(); + for (JsonElement jsonElt : jsonRoot) { + JsonObject jsonProfile = jsonElt.getAsJsonObject(); + QProfile profile = new QProfile(); + profile.setKey(jsonProfile.get("key").getAsString()); + profile.setName(jsonProfile.get("name").getAsString()); + profile.setLanguage(jsonProfile.get("language").getAsString()); + profile.setRulesUpdatedAt(UtcDateUtils.parseDateTime(jsonProfile.get("rulesUpdatedAt").getAsString())); + result.add(profile); } return result; } @@ -66,9 +72,10 @@ public class UsedQProfiles { for (QProfile profile : profiles) { writer .beginObject() - .prop("key", profile.key()) - .prop("language", profile.language()) - .prop("name", profile.name()) + .prop("key", profile.getKey()) + .prop("language", profile.getLanguage()) + .prop("name", profile.getName()) + .prop("rulesUpdatedAt", UtcDateUtils.formatDateTime(profile.getRulesUpdatedAt())) .endObject(); } writer.endArray(); @@ -96,9 +103,9 @@ public class UsedQProfiles { } public Map profilesByKey() { - Map map = new HashMap(); + Map map = new HashMap(); for (QProfile profile : profiles) { - map.put(profile.key(), profile); + map.put(profile.getKey(), profile); } return map; } diff --git a/sonar-batch/src/main/java/org/sonar/batch/rules/DefaultQProfileReferential.java b/sonar-batch/src/main/java/org/sonar/batch/rules/DefaultQProfileReferential.java index 7383b9ec9ec..8aee3971a80 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/rules/DefaultQProfileReferential.java +++ b/sonar-batch/src/main/java/org/sonar/batch/rules/DefaultQProfileReferential.java @@ -20,6 +20,7 @@ package org.sonar.batch.rules; import org.sonar.batch.rule.QProfile; +import org.sonar.core.UtcDateUtils; import org.sonar.core.qualityprofile.db.QualityProfileDao; import org.sonar.core.qualityprofile.db.QualityProfileDto; @@ -40,7 +41,12 @@ public class DefaultQProfileReferential implements QProfilesReferential { if (dto == null) { return null; } - return new QProfile(dto.getKey(), dto.getName(), dto.getLanguage()); + QProfile profile = new QProfile(); + profile.setKey(dto.getKey()); + profile.setName(dto.getName()); + profile.setLanguage(dto.getLanguage()); + profile.setRulesUpdatedAt(UtcDateUtils.parseDateTime(dto.getRulesUpdatedAt())); + return profile; } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java index cae44f3e7c1..25576c8c6fd 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/ActiveRulesProviderTest.java @@ -54,9 +54,9 @@ public class ActiveRulesProviderTest extends AbstractDaoTestCase { setupData("shared"); when(qProfiles.findAll()).thenReturn(Arrays.asList( // 1 rule is enabled on java with severity INFO - new QProfile("java-two", "Java Two", "java"), + new QProfile().setKey("java-two").setName("Java Two").setLanguage("java"), // 1 rule is enabled on php with severity BLOCKER - new QProfile("php-one", "Php One", "php") + new QProfile().setKey("php-one").setName("Php One").setLanguage("php") )); ActiveRulesProvider provider = new ActiveRulesProvider(); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java index ab941f09db7..8c7d2d2bafc 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/ModuleQProfilesTest.java @@ -25,6 +25,7 @@ import org.sonar.api.config.Settings; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.System2; import org.sonar.batch.languages.DeprecatedLanguagesReferential; import org.sonar.batch.languages.LanguagesReferential; import org.sonar.batch.rules.DefaultQProfileReferential; @@ -46,7 +47,7 @@ public class ModuleQProfilesTest extends AbstractDaoTestCase { public void find_profiles() throws Exception { // 4 profiles in db setupData("shared"); - QualityProfileDao dao = new QualityProfileDao(getMyBatis()); + QualityProfileDao dao = new QualityProfileDao(getMyBatis(), System2.INSTANCE); QProfilesReferential ref = new DefaultQProfileReferential(dao); settings.setProperty("sonar.profile.java", "Java One"); @@ -62,19 +63,19 @@ public class ModuleQProfilesTest extends AbstractDaoTestCase { assertThat(moduleQProfiles.findByLanguage("php")).isNotNull(); assertThat(moduleQProfiles.findByLanguage("abap")).isNull(); QProfile javaProfile = qProfiles.get(0); - assertThat(javaProfile.key()).isEqualTo("java-one"); - assertThat(javaProfile.name()).isEqualTo("Java One"); - assertThat(javaProfile.language()).isEqualTo("java"); + assertThat(javaProfile.getKey()).isEqualTo("java-one"); + assertThat(javaProfile.getName()).isEqualTo("Java One"); + assertThat(javaProfile.getLanguage()).isEqualTo("java"); QProfile phpProfile = qProfiles.get(1); - assertThat(phpProfile.key()).isEqualTo("php-one"); - assertThat(phpProfile.name()).isEqualTo("Php One"); - assertThat(phpProfile.language()).isEqualTo("php"); + assertThat(phpProfile.getKey()).isEqualTo("php-one"); + assertThat(phpProfile.getName()).isEqualTo("Php One"); + assertThat(phpProfile.getLanguage()).isEqualTo("php"); } @Test public void supported_deprecated_property() throws Exception { setupData("shared"); - QualityProfileDao dao = new QualityProfileDao(getMyBatis()); + QualityProfileDao dao = new QualityProfileDao(getMyBatis(), System2.INSTANCE); QProfilesReferential ref = new DefaultQProfileReferential(dao); // deprecated property @@ -86,21 +87,21 @@ public class ModuleQProfilesTest extends AbstractDaoTestCase { assertThat(qProfiles).hasSize(2); QProfile javaProfile = qProfiles.get(0); - assertThat(javaProfile.key()).isEqualTo("java-two"); - assertThat(javaProfile.name()).isEqualTo("Java Two"); - assertThat(javaProfile.language()).isEqualTo("java"); + assertThat(javaProfile.getKey()).isEqualTo("java-two"); + assertThat(javaProfile.getName()).isEqualTo("Java Two"); + assertThat(javaProfile.getLanguage()).isEqualTo("java"); // "Java Two" does not exist for PHP -> fallback to sonar.profile.php QProfile phpProfile = qProfiles.get(1); - assertThat(phpProfile.key()).isEqualTo("php-one"); - assertThat(phpProfile.name()).isEqualTo("Php One"); - assertThat(phpProfile.language()).isEqualTo("php"); + assertThat(phpProfile.getKey()).isEqualTo("php-one"); + assertThat(phpProfile.getName()).isEqualTo("Php One"); + assertThat(phpProfile.getLanguage()).isEqualTo("php"); } @Test public void fail_if_unknown_profile() throws Exception { setupData("shared"); - QualityProfileDao dao = new QualityProfileDao(getMyBatis()); + QualityProfileDao dao = new QualityProfileDao(getMyBatis(), System2.INSTANCE); QProfilesReferential ref = new DefaultQProfileReferential(dao); settings.setProperty("sonar.profile.java", "Unknown"); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileDecoratorTest.java index 16003f0a10f..fa46843d94e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileDecoratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileDecoratorTest.java @@ -36,9 +36,9 @@ import static org.mockito.Mockito.*; public class QProfileDecoratorTest { - static final String JAVA_JSON = "{\"key\":\"J1\",\"language\":\"java\",\"name\":\"Java One\"}"; - static final String JAVA2_JSON = "{\"key\":\"J2\",\"language\":\"java\",\"name\":\"Java Two\"}"; - static final String PHP_JSON = "{\"key\":\"P1\",\"language\":\"php\",\"name\":\"Php One\"}"; + static final String JAVA_JSON = "{\"key\":\"J1\",\"language\":\"java\",\"name\":\"Java One\",\"rulesUpdatedAt\":\"2014-01-15T10:00:00+0000\"}"; + static final String JAVA2_JSON = "{\"key\":\"J2\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T10:00:00+0000\"}"; + static final String PHP_JSON = "{\"key\":\"P1\",\"language\":\"php\",\"name\":\"Php One\",\"rulesUpdatedAt\":\"2014-01-15T10:00:00+0000\"}"; Project project = mock(Project.class); Project moduleA = mock(Project.class); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java index fa97b20289e..c0010d823f0 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileEventsDecoratorTest.java @@ -38,7 +38,6 @@ */ package org.sonar.batch.rule; -import org.junit.Ignore; import org.junit.Test; import org.sonar.api.batch.DecoratorContext; import org.sonar.api.batch.Event; @@ -58,15 +57,13 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.same; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; public class QProfileEventsDecoratorTest { - static final String JAVA_V1_JSON = "{\"key\":\"J1\",\"language\":\"java\",\"name\":\"Java One\"}"; - static final String JAVA_V2_JSON = "{\"key\":\"J1\",\"language\":\"java\",\"name\":\"Java One\"}"; + static final String JAVA_V1_JSON = "{\"key\":\"J1\",\"language\":\"java\",\"name\":\"Java One\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}"; + static final String JAVA_V2_JSON = "{\"key\":\"J1\",\"language\":\"java\",\"name\":\"Java One\",\"rulesUpdatedAt\":\"2014-02-20T12:00:00+0000\"}"; + static final String JAVA_OTHER_JSON = "{\"key\":\"J2\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-02-20T12:00:00+0000\"}"; Project project = new Project("myProject"); DecoratorContext decoratorContext = mock(DecoratorContext.class); @@ -75,12 +72,12 @@ public class QProfileEventsDecoratorTest { QProfileEventsDecorator decorator = new QProfileEventsDecorator(timeMachine, languages); @Test - public void shouldExecuteOnProjects() { + public void execute_on_all_projects() { assertThat(decorator.shouldExecuteOnProject(project)).isTrue(); } @Test - public void shouldDoNothingIfNoProfileChange() { + public void do_not_generate_event_if_no_changes() { Measure previousMeasure = new Measure(CoreMetrics.QUALITY_PROFILES, "[" + JAVA_V1_JSON + "]"); Measure newMeasure = new Measure(CoreMetrics.QUALITY_PROFILES, "[" + JAVA_V1_JSON + "]"); @@ -94,8 +91,7 @@ public class QProfileEventsDecoratorTest { } @Test - @Ignore - public void shouldCreateEventIfProfileChange() { + public void generate_event_if_profile_change() { Measure previousMeasure = new Measure(CoreMetrics.QUALITY_PROFILES, "[" + JAVA_V1_JSON + "]"); // Different profile Measure newMeasure = new Measure(CoreMetrics.QUALITY_PROFILES, "[" + JAVA_V2_JSON + "]"); @@ -109,13 +105,33 @@ public class QProfileEventsDecoratorTest { decorator.decorate(project, decoratorContext); verify(decoratorContext).createEvent( - eq("Use Java Other version 1 (Java)"), - eq("Java Other version 1 used for Java"), + eq("Use Java One (Java)"), + eq("Java One used for Java"), same(Event.CATEGORY_PROFILE), any(Date.class)); } @Test - public void shouldNotCreateEventIfFirstAnalysis() { + public void generate_event_if_profile_not_used_anymore() { + Measure previousMeasure = new Measure(CoreMetrics.QUALITY_PROFILES, "[" + JAVA_V1_JSON + "]"); + // Different profile + Measure newMeasure = new Measure(CoreMetrics.QUALITY_PROFILES, "[" + JAVA_OTHER_JSON + "]"); + + when(timeMachine.getMeasures(any(TimeMachineQuery.class))) + .thenReturn(Arrays.asList(previousMeasure)); + when(decoratorContext.getMeasure(CoreMetrics.QUALITY_PROFILES)).thenReturn(newMeasure); + + when(languages.get("java")).thenReturn(Java.INSTANCE); + + decorator.decorate(project, decoratorContext); + + verify(decoratorContext).createEvent( + eq("Stop using Java One (Java)"), + eq("Java One no more used for Java"), + same(Event.CATEGORY_PROFILE), any(Date.class)); + } + + @Test + public void do_not_generate_event_on_first_analysis() { Measure newMeasure = new Measure(CoreMetrics.QUALITY_PROFILES, "[" + JAVA_V1_JSON + "]"); when(decoratorContext.getMeasure(CoreMetrics.QUALITY_PROFILES)).thenReturn(newMeasure); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java index 844a26ee9cc..cae57ace39e 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileSensorTest.java @@ -25,8 +25,10 @@ import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.measures.CoreMetrics; import org.sonar.api.resources.Project; import org.sonar.api.test.IsMeasure; +import org.sonar.core.UtcDateUtils; import java.util.Collections; +import java.util.Date; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.argThat; @@ -34,6 +36,12 @@ import static org.mockito.Mockito.*; public class QProfileSensorTest { + static final Date DATE = UtcDateUtils.parseDateTime("2014-01-15T12:00:00+0000"); + static final QProfile JAVA_PROFILE = new QProfile().setKey("java-two").setName("Java Two").setLanguage("java") + .setRulesUpdatedAt(DATE); + static final QProfile PHP_PROFILE = new QProfile().setKey("php-one").setName("Php One").setLanguage("php") + .setRulesUpdatedAt(DATE); + ModuleQProfiles moduleQProfiles = mock(ModuleQProfiles.class); Project project = mock(Project.class); SensorContext sensorContext = mock(SensorContext.class); @@ -59,8 +67,8 @@ public class QProfileSensorTest { @Test public void mark_profiles_as_used() throws Exception { - when(moduleQProfiles.findByLanguage("java")).thenReturn(new QProfile("java-two", "Java Two", "java")); - when(moduleQProfiles.findByLanguage("php")).thenReturn(new QProfile("php-one", "Php One", "php")); + when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE); + when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE); when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); fs.addLanguages("java", "php", "abap"); @@ -71,8 +79,8 @@ public class QProfileSensorTest { @Test public void store_measures_on_single_lang_module() throws Exception { - when(moduleQProfiles.findByLanguage("java")).thenReturn(new QProfile("java-two", "Java Two", "java")); - when(moduleQProfiles.findByLanguage("php")).thenReturn(new QProfile("php-one", "Php One", "php")); + when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE); + when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE); when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); fs.addLanguages("java"); @@ -81,13 +89,14 @@ public class QProfileSensorTest { sensor.analyse(project, sensorContext); verify(sensorContext).saveMeasure( - argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\"}]"))); + argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, + "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]"))); } @Test public void store_measures_on_multi_lang_module() throws Exception { - when(moduleQProfiles.findByLanguage("java")).thenReturn(new QProfile("java-two", "Java Two", "java")); - when(moduleQProfiles.findByLanguage("php")).thenReturn(new QProfile("php-one", "Php One", "php")); + when(moduleQProfiles.findByLanguage("java")).thenReturn(JAVA_PROFILE); + when(moduleQProfiles.findByLanguage("php")).thenReturn(PHP_PROFILE); when(moduleQProfiles.findByLanguage("abap")).thenReturn(null); fs.addLanguages("java", "php"); @@ -97,6 +106,7 @@ public class QProfileSensorTest { verify(sensorContext).saveMeasure( argThat(new IsMeasure(CoreMetrics.QUALITY_PROFILES, - "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\"},{\"key\":\"php-one\",\"language\":\"php\",\"name\":\"Php One\"}]"))); + "[{\"key\":\"java-two\",\"language\":\"java\",\"name\":\"Java Two\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}," + + "{\"key\":\"php-one\",\"language\":\"php\",\"name\":\"Php One\",\"rulesUpdatedAt\":\"2014-01-15T12:00:00+0000\"}]"))); } } diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java index 3b9cc15c4b7..da457212cd5 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/QProfileVerifierTest.java @@ -42,9 +42,9 @@ public class QProfileVerifierTest { @Before public void before() { profiles = mock(ModuleQProfiles.class); - QProfile javaProfile = new QProfile("p1", "My Java profile", "java"); + QProfile javaProfile = new QProfile().setKey("p1").setName("My Java profile").setLanguage("java"); when(profiles.findByLanguage("java")).thenReturn(javaProfile); - QProfile cobolProfile = new QProfile("p2", "My Cobol profile", "cobol"); + QProfile cobolProfile = new QProfile().setKey("p2").setName("My Cobol profile").setLanguage("cobol"); when(profiles.findByLanguage("cobol")).thenReturn(cobolProfile); } diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java index f45636cca05..26331f5e0b7 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/RulesProfileProviderTest.java @@ -42,7 +42,7 @@ public class RulesProfileProviderTest { @Test public void merge_profiles() throws Exception { - QProfile qProfile = new QProfile("java-sw", "Sonar way", "java"); + QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); when(qProfiles.findAll()).thenReturn(Arrays.asList(qProfile)); RulesProfile profile = provider.provide(qProfiles, activeRules, ruleFinder, settings); @@ -64,7 +64,7 @@ public class RulesProfileProviderTest { public void keep_compatibility_with_single_language_projects() throws Exception { settings.setProperty("sonar.language", "java"); - QProfile qProfile = new QProfile("java-sw", "Sonar way", "java"); + QProfile qProfile = new QProfile().setKey("java-sw").setName("Sonar way").setLanguage("java"); when(qProfiles.findByLanguage("java")).thenReturn(qProfile); RulesProfile profile = provider.provide(qProfiles, activeRules, ruleFinder, settings); diff --git a/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java b/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java index 05ea1617306..e3eba53ad2a 100644 --- a/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java +++ b/sonar-batch/src/test/java/org/sonar/batch/rule/UsedQProfilesTest.java @@ -20,6 +20,7 @@ package org.sonar.batch.rule; import org.junit.Test; +import org.sonar.core.UtcDateUtils; import java.util.Arrays; import java.util.Map; @@ -28,25 +29,30 @@ import static org.fest.assertions.Assertions.assertThat; public class UsedQProfilesTest { + static final String JAVA_JSON = "{\"key\":\"p1\",\"language\":\"java\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-01-15T00:00:00+0000\"}"; + static final String PHP_JSON = "{\"key\":\"p2\",\"language\":\"php\",\"name\":\"Sonar Way\",\"rulesUpdatedAt\":\"2014-02-20T00:00:00+0000\"}"; + @Test public void from_and_to_json() throws Exception { - QProfile java = new QProfile("p1", "Sonar Way", "java"); - QProfile php = new QProfile("p2", "Sonar Way", "php"); + QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java") + .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-01-15T00:00:00+0000")); + QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php") + .setRulesUpdatedAt(UtcDateUtils.parseDateTime("2014-02-20T00:00:00+0000")); UsedQProfiles used = new UsedQProfiles().add(java).add(php); - String json = "[{\"key\":\"p1\",\"language\":\"java\",\"name\":\"Sonar Way\"},{\"key\":\"p2\",\"language\":\"php\",\"name\":\"Sonar Way\"}]"; + String json = "[" + JAVA_JSON + "," + PHP_JSON + "]"; assertThat(used.toJson()).isEqualTo(json); used = UsedQProfiles.fromJson(json); assertThat(used.profiles()).hasSize(2); - assertThat(used.profiles().first().key()).isEqualTo("p1"); - assertThat(used.profiles().last().key()).isEqualTo("p2"); + assertThat(used.profiles().first().getKey()).isEqualTo("p1"); + assertThat(used.profiles().last().getKey()).isEqualTo("p2"); } @Test public void do_not_duplicate_profiles() throws Exception { - QProfile java = new QProfile("p1", "Sonar Way", "java"); - QProfile php = new QProfile("p2", "Sonar Way", "php"); + QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java"); + QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php"); UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php)); assertThat(used.profiles()).hasSize(2); @@ -54,8 +60,8 @@ public class UsedQProfilesTest { @Test public void group_profiles_by_key() throws Exception { - QProfile java = new QProfile("p1", "Sonar Way", "java"); - QProfile php = new QProfile("p2", "Sonar Way", "php"); + QProfile java = new QProfile().setKey("p1").setName("Sonar Way").setLanguage("java"); + QProfile php = new QProfile().setKey("p2").setName("Sonar Way").setLanguage("php"); UsedQProfiles used = new UsedQProfiles().addAll(Arrays.asList(java, java, php)); Map map = used.profilesByKey(); diff --git a/sonar-batch/src/test/resources/org/sonar/batch/rule/ModuleQProfilesTest/shared.xml b/sonar-batch/src/test/resources/org/sonar/batch/rule/ModuleQProfilesTest/shared.xml index 7dd90b77196..468609d2039 100644 --- a/sonar-batch/src/test/resources/org/sonar/batch/rule/ModuleQProfilesTest/shared.xml +++ b/sonar-batch/src/test/resources/org/sonar/batch/rule/ModuleQProfilesTest/shared.xml @@ -1,11 +1,19 @@ - + - + - + - + diff --git a/sonar-core/pom.xml b/sonar-core/pom.xml index 631bf0b1a95..331c28cede2 100644 --- a/sonar-core/pom.xml +++ b/sonar-core/pom.xml @@ -156,14 +156,7 @@ com.google.code.bean-matchers bean-matchers - 0.9 test - - - org.hamcrest - hamcrest-core - - diff --git a/sonar-core/src/main/java/org/sonar/core/UtcDateUtils.java b/sonar-core/src/main/java/org/sonar/core/UtcDateUtils.java new file mode 100644 index 00000000000..298fda02e34 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/UtcDateUtils.java @@ -0,0 +1,54 @@ +/* + * 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; + +import org.sonar.api.utils.DateUtils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class UtcDateUtils { + + private UtcDateUtils() { + // only static stuff + } + + public static String formatDateTime(Date date) { + SimpleDateFormat format = newFormat(); + return format.format(date); + } + + public static Date parseDateTime(String s) { + try { + SimpleDateFormat format = newFormat(); + return format.parse(s); + } catch (ParseException e) { + throw new IllegalArgumentException("Fail to parse date: " + s, e); + } + } + + private static SimpleDateFormat newFormat() { + SimpleDateFormat format = new SimpleDateFormat(DateUtils.DATETIME_FORMAT); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/dialect/DialectUtils.java b/sonar-core/src/main/java/org/sonar/core/persistence/dialect/DialectUtils.java index 9c9579932b6..1ad06d54415 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/dialect/DialectUtils.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/dialect/DialectUtils.java @@ -24,12 +24,14 @@ import com.google.common.collect.Iterators; import org.apache.commons.lang.StringUtils; import org.sonar.api.utils.SonarException; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.util.NoSuchElementException; public final class DialectUtils { private DialectUtils() { + // only static stuff } private static final Dialect[] DIALECTS = new Dialect[] {new H2(), new MySql(), new Oracle(), new PostgreSql(), new MsSql()}; @@ -42,6 +44,7 @@ public final class DialectUtils { return match; } + @CheckForNull private static Dialect findByJdbcUrl(final String jdbcConnectionUrl) { return findDialect(new Predicate() { public boolean apply(@Nullable Dialect dialect) { @@ -50,6 +53,7 @@ public final class DialectUtils { }); } + @CheckForNull private static Dialect findById(final String dialectId) { return findDialect(new Predicate() { public boolean apply(@Nullable Dialect dialect) { @@ -58,6 +62,7 @@ public final class DialectUtils { }); } + @CheckForNull private static Dialect findDialect(Predicate predicate) { try { return Iterators.find(Iterators.forArray(DIALECTS), predicate); diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java index d27b8c95c4e..d3bde433e58 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/Migration44Mapper.java @@ -22,6 +22,7 @@ package org.sonar.core.persistence.migration.v44; import org.apache.ibatis.annotations.Param; import javax.annotation.CheckForNull; + import java.util.Date; import java.util.List; @@ -29,14 +30,27 @@ public interface Migration44Mapper { // migration of measures "profile" and "profile_version" List selectProfileMeasures(); + int selectProfileVersion(long snapshotId); - @CheckForNull Date selectProfileVersionDate(@Param("profileId") int profileId, @Param("profileVersion") int profileVersion); + + @CheckForNull + Date selectProfileVersionDate(@Param("profileId") int profileId, @Param("profileVersion") int profileVersion); + void updateProfileMeasure(@Param("measureId") long measureId, @Param("json") String json); + @CheckForNull + QProfileDto44 selectProfileById(int id); + // creation of columns RULES_PROFILES.CREATED_AT and UPDATED_AT - @CheckForNull Date selectProfileCreatedAt(int profileId); - @CheckForNull Date selectProfileUpdatedAt(int profileId); - void updateProfileDates(@Param("profileId") int profileId, @Param("createdAt") Date createdAt, @Param("updatedAt") Date updatedAt); + @CheckForNull + Date selectProfileCreatedAt(int profileId); + + @CheckForNull + Date selectProfileUpdatedAt(int profileId); + + void updateProfileDates(@Param("profileId") int profileId, + @Param("createdAt") Date createdAt, @Param("updatedAt") Date updatedAt, + @Param("rulesUpdatedAt") String rulesUpdatedAt); // migrate changeLog to Activities List selectActiveRuleChange(@Param("enabled") Boolean enabled); diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/QProfileDto44.java b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/QProfileDto44.java new file mode 100644 index 00000000000..e69d23c1cb5 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/QProfileDto44.java @@ -0,0 +1,60 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.core.persistence.migration.v44; + +public class QProfileDto44 { + + private Integer id; + private String kee; + private String name; + private String language; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getKee() { + return kee; + } + + public void setKee(String kee) { + this.kee = kee; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/package-info.java b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/package-info.java new file mode 100644 index 00000000000..efd32c18d20 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/persistence/migration/v44/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@ParametersAreNonnullByDefault +package org.sonar.core.persistence.migration.v44; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java index 25724a1459f..45189d64df9 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java +++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDao.java @@ -23,6 +23,7 @@ package org.sonar.core.qualityprofile.db; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import org.sonar.api.ServerComponent; +import org.sonar.api.utils.System2; import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.DaoComponent; import org.sonar.core.persistence.DbSession; @@ -30,14 +31,17 @@ import org.sonar.core.persistence.MyBatis; import javax.annotation.CheckForNull; +import java.util.Date; import java.util.List; public class QualityProfileDao implements ServerComponent, DaoComponent { private final MyBatis mybatis; + private final System2 system; - public QualityProfileDao(MyBatis mybatis) { + public QualityProfileDao(MyBatis mybatis, System2 system) { this.mybatis = mybatis; + this.system = system; } @CheckForNull @@ -67,6 +71,9 @@ public class QualityProfileDao implements ServerComponent, DaoComponent { private void doInsert(QualityProfileMapper mapper, QualityProfileDto profile) { Preconditions.checkArgument(profile.getId() == null, "Quality profile is already persisted (got id %d)", profile.getId()); + Date now = new Date(system.now()); + profile.setCreatedAt(now); + profile.setUpdatedAt(now); mapper.insert(profile); } @@ -94,6 +101,7 @@ public class QualityProfileDao implements ServerComponent, DaoComponent { private void doUpdate(QualityProfileMapper mapper, QualityProfileDto profile) { Preconditions.checkArgument(profile.getId() != null, "Quality profile is not persisted"); + profile.setUpdatedAt(new Date(system.now())); mapper.update(profile); } diff --git a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDto.java b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDto.java index bc7224a5fc3..030d67973dc 100644 --- a/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDto.java +++ b/sonar-core/src/main/java/org/sonar/core/qualityprofile/db/QualityProfileDto.java @@ -20,6 +20,7 @@ package org.sonar.core.qualityprofile.db; +import org.sonar.core.UtcDateUtils; import org.sonar.core.persistence.Dto; import javax.annotation.CheckForNull; @@ -33,7 +34,8 @@ public class QualityProfileDto extends Dto { private String name; private String language; private String parentKee; - private Date createdAt, updatedAt, rulesUpdatedAt; + private Date createdAt, updatedAt; + private String rulesUpdatedAt; /** * @deprecated use {@link #createFor(String)} @@ -114,6 +116,24 @@ public class QualityProfileDto extends Dto { this.updatedAt = updatedAt; } + public String getRulesUpdatedAt() { + return rulesUpdatedAt; + } + + public Date getRulesUpdatedAtAsDate() { + return UtcDateUtils.parseDateTime(rulesUpdatedAt); + } + + public QualityProfileDto setRulesUpdatedAt(String s) { + this.rulesUpdatedAt = s; + return this; + } + + public QualityProfileDto setRulesUpdatedAt(Date d) { + this.rulesUpdatedAt = UtcDateUtils.formatDateTime(d); + return this; + } + public static QualityProfileDto createFor(String key) { return new QualityProfileDto().setKee(key); } diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml b/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml index 8adb47e20c7..48669de583b 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/migration/v44/Migration44Mapper.xml @@ -7,7 +7,7 @@ select pm.id as id, pm.value as profileId, pm.snapshot_id as snapshotId from project_measures pm inner join metrics m on m.id=pm.metric_id and m.name='profile' - inner join snapshots s on s.islast=#{_true} and pm.snapshot_id=s.id and s.scope='PRJ' + inner join snapshots s on s.islast=${_true} and pm.snapshot_id=s.id and s.scope='PRJ' where pm.value is not null @@ -18,6 +18,12 @@ where pm.value is not null and s.id=#{id} + +