All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.server.debt.DebtModelBackup Maven / Gradle / Ivy

There is a newer version: 7.2.1
Show newest version
/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.server.debt;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import java.util.Date;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.ServerSide;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.ValidationMessages;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.MyBatis;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
import org.sonar.server.rule.RuleDefinitionsLoader;
import org.sonar.server.rule.RuleOperations;
import org.sonar.server.rule.index.RuleIndexer;
import org.sonar.server.user.UserSession;

import static com.google.common.collect.Lists.newArrayList;

@ServerSide
public class DebtModelBackup {

  private static final Logger LOG = Loggers.get(DebtModelBackup.class);

  private final DbClient dbClient;
  private final RuleOperations ruleOperations;
  private final DebtRulesXMLImporter rulesXMLImporter;
  private final DebtModelXMLExporter debtModelXMLExporter;
  private final RuleDefinitionsLoader defLoader;
  private final System2 system2;
  private final UserSession userSession;
  private final RuleIndexer ruleIndexer;

  public DebtModelBackup(DbClient dbClient, RuleOperations ruleOperations,
    DebtRulesXMLImporter rulesXMLImporter,
    DebtModelXMLExporter debtModelXMLExporter, RuleDefinitionsLoader defLoader, System2 system2, UserSession userSession, RuleIndexer ruleIndexer) {
    this.dbClient = dbClient;
    this.ruleOperations = ruleOperations;
    this.rulesXMLImporter = rulesXMLImporter;
    this.debtModelXMLExporter = debtModelXMLExporter;
    this.defLoader = defLoader;
    this.system2 = system2;
    this.userSession = userSession;
    this.ruleIndexer = ruleIndexer;
  }

  public String backup() {
    return backupFromLanguage(null);
  }

  public String backup(String languageKey) {
    return backupFromLanguage(languageKey);
  }

  private String backupFromLanguage(@Nullable String languageKey) {
    checkPermission();

    DbSession session = dbClient.openSession(false);
    try {
      List rules = newArrayList();
      for (RuleDto rule : dbClient.ruleDao().selectEnabled(session)) {
        if (languageKey != null && !languageKey.equals(rule.getLanguage())) {
          continue;
        }
        RuleDebt ruleDebt = toRuleDebt(rule);
        if (ruleDebt != null) {
          rules.add(ruleDebt);
        }
      }
      return debtModelXMLExporter.export(rules);
    } finally {
      MyBatis.closeQuietly(session);
    }
  }

  /**
   * Reset from provided model
   */
  public void reset() {
    checkPermission();

    long updateDate = system2.now();
    DbSession session = dbClient.openSession(false);
    try {
      // Restore rules
      List ruleDtos = dbClient.ruleDao().selectEnabled(session);
      if (!ruleDtos.isEmpty()) {

        // Load default rule definitions
        RulesDefinition.Context context = defLoader.load();
        List rules = newArrayList();
        for (RulesDefinition.Repository repoDef : context.repositories()) {
          rules.addAll(repoDef.rules());
        }

        resetRules(ruleDtos, rules, updateDate, session);
      }

      session.commit();
      ruleIndexer.index();
    } finally {
      MyBatis.closeQuietly(session);
    }
  }

  private void resetRules(List ruleDtos, List rules, long updateDate, DbSession session) {
    for (RuleDto rule : ruleDtos) {
      // Restore default debt definitions

      RulesDefinition.Rule ruleDef;
      Integer ruleTemplateId = rule.getTemplateId();
      if (ruleTemplateId != null) {
        RuleDto templateRule = rule(ruleTemplateId, ruleDtos);
        ruleDef = ruleDef(templateRule.getRepositoryKey(), templateRule.getRuleKey(), rules);
      } else {
        ruleDef = ruleDef(rule.getRepositoryKey(), rule.getRuleKey(), rules);
      }

      if (ruleDef != null) {
        DebtRemediationFunction remediationFunction = ruleDef.debtRemediationFunction();
        boolean hasDebtDefinition = remediationFunction != null;

        rule.setDefaultRemediationFunction(hasDebtDefinition ? remediationFunction.type().name() : null);
        rule.setDefaultRemediationGapMultiplier(hasDebtDefinition ? remediationFunction.gapMultiplier() : null);
        rule.setDefaultRemediationBaseEffort(hasDebtDefinition ? remediationFunction.baseEffort() : null);
      }

      // Reset overridden debt definitions
      rule.setRemediationFunction(null);
      rule.setRemediationGapMultiplier(null);
      rule.setRemediationBaseEffort(null);
      rule.setUpdatedAt(updateDate);
      dbClient.ruleDao().update(session, rule);
    }
  }

  /**
   * Restore model from a given XML model (characteristics and rule debt are restored from XML)
   */
  public ValidationMessages restoreFromXml(String xml) {
    return restoreXmlModel(xml, null);
  }

  /**
   * Restore model from a given XML model and a given language (only debt of rules on given language are restored from XML)
   */
  public ValidationMessages restoreFromXml(String xml, final String languageKey) {
    return restoreXmlModel(xml, languageKey);
  }

  private ValidationMessages restoreXmlModel(String xml, @Nullable final String languageKey) {
    checkPermission();

    ValidationMessages validationMessages = ValidationMessages.create();
    Date updateDate = new Date(system2.now());
    DbSession session = dbClient.openSession(false);
    try {
      restoreRules(rules(languageKey, session), rulesXMLImporter.importXML(xml, validationMessages), validationMessages, updateDate, session);

      session.commit();
      ruleIndexer.index();
    } catch (IllegalArgumentException e) {
      LOG.debug("Error when restoring the model", e);
      validationMessages.addErrorText(e.getMessage());
    } finally {
      MyBatis.closeQuietly(session);
    }
    return validationMessages;
  }

  private void restoreRules(List rules, List ruleDebts,
    ValidationMessages validationMessages, Date updateDate, DbSession session) {
    for (RuleDto rule : rules) {
      RuleDebt ruleDebt = ruleDebt(rule.getRepositoryKey(), rule.getRuleKey(), ruleDebts);
      ruleOperations.updateRule(rule,
        ruleDebt != null ? ruleDebt.function() : null,
        ruleDebt != null ? ruleDebt.coefficient() : null,
        ruleDebt != null ? ruleDebt.offset() : null, session);
      rule.setUpdatedAt(updateDate.getTime());
      ruleDebts.remove(ruleDebt);
    }

    for (RuleDebt ruleDebt : ruleDebts) {
      validationMessages.addWarningText(String.format("The rule '%s' does not exist.", ruleDebt.ruleKey()));
    }
  }

  private List rules(@Nullable String languageKey, DbSession session) {
    List rules = dbClient.ruleDao().selectEnabled(session);
    if (languageKey == null) {
      return rules;
    }
    return newArrayList(Iterables.filter(rules, new RuleDtoMatchLanguage(languageKey)));
  }

  @CheckForNull
  private static RuleDebt ruleDebt(String ruleRepo, String ruleKey, List ruleDebts) {
    if (ruleDebts.isEmpty()) {
      return null;
    }
    return Iterables.find(ruleDebts, new RuleDebtMatchRuleRepoAndRuleKey(ruleRepo, ruleKey), null);
  }

  private static RuleDto rule(int id, List rules) {
    return Iterables.find(rules, new RuleDtoMatchId(id));
  }

  @CheckForNull
  private static RulesDefinition.Rule ruleDef(String ruleRepo, String ruleKey, List rules) {
    return Iterables.find(rules, new RuleDefMatchRuleRepoAndRuleKey(ruleRepo, ruleKey), null);
  }

  @CheckForNull
  private static RuleDebt toRuleDebt(RuleDto rule) {
    RuleDebt ruleDebt = new RuleDebt().setRuleKey(RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()));
    String overriddenFunction = rule.getRemediationFunction();
    String defaultFunction = rule.getDefaultRemediationFunction();
    if (overriddenFunction != null) {
      ruleDebt.setFunction(overriddenFunction);
      ruleDebt.setCoefficient(rule.getRemediationGapMultiplier());
      ruleDebt.setOffset(rule.getRemediationBaseEffort());
      return ruleDebt;
    } else if (defaultFunction != null) {
      ruleDebt.setFunction(defaultFunction);
      ruleDebt.setCoefficient(rule.getDefaultRemediationGapMultiplier());
      ruleDebt.setOffset(rule.getDefaultRemediationBaseEffort());
      return ruleDebt;
    }
    return null;
  }

  private void checkPermission() {
    userSession.checkPermission(GlobalPermissions.SYSTEM_ADMIN);
  }

  private static class RuleDtoMatchLanguage implements Predicate {
    private final String languageKey;

    public RuleDtoMatchLanguage(String languageKey) {
      this.languageKey = languageKey;
    }

    @Override
    public boolean apply(@Nonnull RuleDto input) {
      return languageKey.equals(input.getLanguage());
    }
  }

  private static class RuleDebtMatchRuleRepoAndRuleKey implements Predicate {

    private final String ruleRepo;
    private final String ruleKey;

    public RuleDebtMatchRuleRepoAndRuleKey(String ruleRepo, String ruleKey) {
      this.ruleRepo = ruleRepo;
      this.ruleKey = ruleKey;
    }

    @Override
    public boolean apply(@Nullable RuleDebt input) {
      return input != null && ruleRepo.equals(input.ruleKey().repository()) && ruleKey.equals(input.ruleKey().rule());
    }
  }

  private static class RuleDtoMatchId implements Predicate {
    private final int id;

    public RuleDtoMatchId(int id) {
      this.id = id;
    }

    @Override
    public boolean apply(@Nonnull RuleDto input) {
      return id == input.getId();
    }
  }

  private static class RuleDefMatchRuleRepoAndRuleKey implements Predicate {

    private final String ruleRepo;
    private final String ruleKey;

    public RuleDefMatchRuleRepoAndRuleKey(String ruleRepo, String ruleKey) {
      this.ruleRepo = ruleRepo;
      this.ruleKey = ruleKey;
    }

    @Override
    public boolean apply(@Nonnull RulesDefinition.Rule input) {
      return ruleRepo.equals(input.repository().key()) && ruleKey.equals(input.key());
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy