From 9f3af63651cabbe428e38fb9d99659e0e44fcbea Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Mon, 5 Feb 2018 15:14:28 +0100 Subject: [PATCH] SONAR-10311 support rule re-keying in case of plugin downgrade --- .../org/sonar/server/rule/RegisterRules.java | 77 +++++++++++++------ 1 file changed, 55 insertions(+), 22 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java index 01aab36cadd..af1de2cba2b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -76,6 +76,7 @@ import static java.lang.String.format; import static java.util.Collections.emptySet; import static org.sonar.core.util.stream.MoreCollectors.toList; import static org.sonar.core.util.stream.MoreCollectors.toSet; +import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; /** * Register rules at server startup @@ -117,7 +118,6 @@ public class RegisterRules implements Startable { RulesDefinition.Context ruleDefinitionContext = defLoader.load(); List repositories = getRepositories(ruleDefinitionContext); RegisterRulesContext registerRulesContext = createRegisterRulesContext(dbSession); - Map> existingDeprecatedRuleKeys = loadDeprecatedRuleKeys(dbSession); verifyRuleKeyConsistency(repositories); @@ -128,7 +128,7 @@ public class RegisterRules implements Startable { if (noTemplateRuleWithOrganizationsEnabled(registerRulesContext, orgsEnabled, ruleDef)) { continue; } - registerRule(registerRulesContext, ruleDef, existingDeprecatedRuleKeys, dbSession); + registerRule(registerRulesContext, ruleDef, dbSession); } dbSession.commit(); } @@ -142,7 +142,7 @@ public class RegisterRules implements Startable { // to be updated. Only a single DB commit should be executed. ruleIndexer.commitAndIndex(dbSession, registerRulesContext.getAllModified().map(RuleDefinitionDto::getId).collect(toSet())); activeRuleIndexer.commitAndIndex(dbSession, changes); - registerRulesContext.getRenamed().forEach(e -> LOG.info("rule {} re-keyed to {}", e.getValue(), e.getKey().getKey())); + registerRulesContext.getRenamed().forEach(e -> LOG.info("Rule {} re-keyed to {}", e.getValue(), e.getKey().getKey())); profiler.stopDebug(); webServerRuleFinder.startCaching(); @@ -164,8 +164,9 @@ public class RegisterRules implements Startable { private RegisterRulesContext createRegisterRulesContext(DbSession dbSession) { Map allRules = dbClient.ruleDao().selectAllDefinitions(dbSession) .stream() - .collect(MoreCollectors.uniqueIndex(RuleDefinitionDto::getKey)); - return new RegisterRulesContext(allRules); + .collect(uniqueIndex(RuleDefinitionDto::getKey)); + Map> existingDeprecatedKeysById = loadDeprecatedRuleKeys(dbSession); + return new RegisterRulesContext(allRules, existingDeprecatedKeysById); } private Map> loadDeprecatedRuleKeys(DbSession dbSession) { @@ -195,6 +196,8 @@ public class RegisterRules implements Startable { // initial immutable data private final Map dbRules; private final Set known; + private final Map> dbDeprecatedKeysById; + private final Map dbRulesByDbDeprecatedKey; // mutable data private final Set created = new HashSet<>(); private final Map renamed = new HashMap<>(); @@ -202,17 +205,48 @@ public class RegisterRules implements Startable { private final Set unchanged = new HashSet<>(); private final Set removed = new HashSet<>(); - private RegisterRulesContext(Map dbRules) { + private RegisterRulesContext(Map dbRules, Map> dbDeprecatedKeysById) { this.dbRules = ImmutableMap.copyOf(dbRules); this.known = ImmutableSet.copyOf(dbRules.values()); + this.dbDeprecatedKeysById = dbDeprecatedKeysById; + this.dbRulesByDbDeprecatedKey = buildDbRulesByDbDeprecatedKey(dbDeprecatedKeysById, dbRules); + } + + private static Map buildDbRulesByDbDeprecatedKey(Map> dbDeprecatedKeysById, + Map dbRules) { + Map dbRulesByRuleId = dbRules.values().stream() + .collect(uniqueIndex(RuleDefinitionDto::getId)); + + ImmutableMap.Builder builder = ImmutableMap.builder(); + for (Map.Entry> entry : dbDeprecatedKeysById.entrySet()) { + Integer ruleId = entry.getKey(); + RuleDefinitionDto rule = dbRulesByRuleId.get(ruleId); + if (rule == null) { + LOG.warn("Could not retrieve rule with id %s referenced by a deprecated rule key. " + + "The following deprecated rule keys seem to be referencing a non-existing rule", + ruleId, entry.getValue()); + } else { + entry.getValue().forEach(d -> builder.put(d.getOldRuleKeyAsRuleKey(), rule)); + } + } + return builder.build(); } private Optional getDbRuleFor(RulesDefinition.Rule ruleDef) { RuleKey ruleKey = RuleKey.of(ruleDef.repository().key(), ruleDef.key()); - return Stream.concat(Stream.of(ruleKey), ruleDef.deprecatedRuleKeys().stream()) + Optional res = Stream.concat(Stream.of(ruleKey), ruleDef.deprecatedRuleKeys().stream()) .map(dbRules::get) .filter(Objects::nonNull) .findFirst(); + // may occur in case of plugin downgrade + if (!res.isPresent()) { + return Optional.ofNullable(dbRulesByDbDeprecatedKey.get(ruleKey)); + } + return res; + } + + private Set getDBDeprecatedKeysFor(RuleDefinitionDto rule) { + return dbDeprecatedKeysById.getOrDefault(rule.getId(), emptySet()); } private Stream getRemaining() { @@ -298,43 +332,42 @@ public class RegisterRules implements Startable { // nothing } - private void registerRule(RegisterRulesContext recorder, RulesDefinition.Rule ruleDef, - Map> existingDeprecatedRuleKeys, DbSession session) { + private void registerRule(RegisterRulesContext context, RulesDefinition.Rule ruleDef, DbSession session) { RuleKey ruleKey = RuleKey.of(ruleDef.repository().key(), ruleDef.key()); - RuleDefinitionDto ruleDefinitionDto = recorder.getDbRuleFor(ruleDef) + RuleDefinitionDto ruleDefinitionDto = context.getDbRuleFor(ruleDef) .orElseGet(() -> { RuleDefinitionDto newRule = createRuleDto(ruleDef, session); - recorder.created(newRule); + context.created(newRule); return newRule; }); // we must detect renaming __before__ we modify the DTO if (!ruleDefinitionDto.getKey().equals(ruleKey)) { - recorder.renamed(ruleDefinitionDto); + context.renamed(ruleDefinitionDto); ruleDefinitionDto.setRuleKey(ruleKey); } if (mergeRule(ruleDef, ruleDefinitionDto)) { - recorder.updated(ruleDefinitionDto); + context.updated(ruleDefinitionDto); } if (mergeDebtDefinitions(ruleDef, ruleDefinitionDto)) { - recorder.updated(ruleDefinitionDto); + context.updated(ruleDefinitionDto); } if (mergeTags(ruleDef, ruleDefinitionDto)) { - recorder.updated(ruleDefinitionDto); + context.updated(ruleDefinitionDto); } - if (recorder.isUpdated(ruleDefinitionDto) || recorder.isRenamed(ruleDefinitionDto)) { + if (context.isUpdated(ruleDefinitionDto) || context.isRenamed(ruleDefinitionDto)) { update(session, ruleDefinitionDto); - } else if (!recorder.isCreated(ruleDefinitionDto)) { - recorder.unchanged(ruleDefinitionDto); + } else if (!context.isCreated(ruleDefinitionDto)) { + context.unchanged(ruleDefinitionDto); } mergeParams(ruleDef, ruleDefinitionDto, session); - updateDeprecatedKeys(ruleDef, ruleDefinitionDto, existingDeprecatedRuleKeys, session); + updateDeprecatedKeys(context, ruleDef, ruleDefinitionDto, session); } private RuleDefinitionDto createRuleDto(RulesDefinition.Rule ruleDef, DbSession session) { @@ -545,11 +578,11 @@ public class RegisterRules implements Startable { return changed; } - private void updateDeprecatedKeys(RulesDefinition.Rule ruleDef, RuleDefinitionDto rule, - Map> existingDeprecatedKeysById, DbSession dbSession) { + private void updateDeprecatedKeys(RegisterRulesContext context, RulesDefinition.Rule ruleDef, RuleDefinitionDto rule, + DbSession dbSession) { Set deprecatedRuleKeysFromDefinition = SingleDeprecatedRuleKey.from(ruleDef); - Set deprecatedRuleKeysFromDB = existingDeprecatedKeysById.getOrDefault(rule.getId(), emptySet()); + Set deprecatedRuleKeysFromDB = context.getDBDeprecatedKeysFor(rule); // DeprecatedKeys that must be deleted List uuidsToBeDeleted = difference(deprecatedRuleKeysFromDB, deprecatedRuleKeysFromDefinition).stream() -- 2.39.5