Package org.sonar.server.debt

Source Code of org.sonar.server.debt.DebtModelBackup

/*
* 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.debt;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.ServerComponent;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.ValidationMessages;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.debt.DebtModelXMLExporter.DebtModel;
import org.sonar.server.debt.DebtModelXMLExporter.RuleDebt;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.rule.RuleDefinitionsLoader;
import org.sonar.server.rule.RuleOperations;
import org.sonar.server.user.UserSession;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.io.Reader;
import java.util.Date;
import java.util.List;

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

public class DebtModelBackup implements ServerComponent {

  private static final Logger LOG = LoggerFactory.getLogger(DebtModelBackup.class);

  private final DbClient dbClient;
  private final DebtModelOperations debtModelOperations;
  private final RuleOperations ruleOperations;
  private final DebtModelPluginRepository debtModelPluginRepository;
  private final DebtCharacteristicsXMLImporter characteristicsXMLImporter;
  private final DebtRulesXMLImporter rulesXMLImporter;
  private final DebtModelXMLExporter debtModelXMLExporter;
  private final RuleDefinitionsLoader defLoader;
  private final System2 system2;

  public DebtModelBackup(DbClient dbClient, DebtModelOperations debtModelOperations, RuleOperations ruleOperations,
                         DebtModelPluginRepository debtModelPluginRepository, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
                         DebtModelXMLExporter debtModelXMLExporter, RuleDefinitionsLoader defLoader) {
    this(dbClient, debtModelOperations, ruleOperations, debtModelPluginRepository, characteristicsXMLImporter, rulesXMLImporter, debtModelXMLExporter,
      defLoader, System2.INSTANCE);
  }

  @VisibleForTesting
  DebtModelBackup(DbClient dbClient, DebtModelOperations debtModelOperations, RuleOperations ruleOperations,
                  DebtModelPluginRepository debtModelPluginRepository, DebtCharacteristicsXMLImporter characteristicsXMLImporter, DebtRulesXMLImporter rulesXMLImporter,
                  DebtModelXMLExporter debtModelXMLExporter, RuleDefinitionsLoader defLoader, System2 system2) {
    this.dbClient = dbClient;
    this.debtModelOperations = debtModelOperations;
    this.ruleOperations = ruleOperations;
    this.debtModelPluginRepository = debtModelPluginRepository;
    this.characteristicsXMLImporter = characteristicsXMLImporter;
    this.rulesXMLImporter = rulesXMLImporter;
    this.debtModelXMLExporter = debtModelXMLExporter;
    this.defLoader = defLoader;
    this.system2 = system2;
  }

  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 {
      DebtModel debtModel = new DebtModel();
      List<CharacteristicDto> characteristicDtos = dbClient.debtCharacteristicDao().selectEnabledCharacteristics(session);
      for (CharacteristicDto characteristicDto : characteristicDtos) {
        if (characteristicDto.getParentId() == null) {
          debtModel.addRootCharacteristic(toDebtCharacteristic(characteristicDto));
          for (CharacteristicDto sub : subCharacteristics(characteristicDto.getId(), characteristicDtos)) {
            debtModel.addSubCharacteristic(toDebtCharacteristic(sub), characteristicDto.getKey());
          }
        }
      }

      List<RuleDebt> rules = newArrayList();
      for (RuleDto rule : dbClient.ruleDao().selectEnabledAndNonManual(session)) {
        if (languageKey == null || languageKey.equals(rule.getLanguage())) {
          RuleDebt ruleDebt = toRuleDebt(rule, debtModel);
          if (ruleDebt != null) {
            rules.add(ruleDebt);
          }
        }
      }
      return debtModelXMLExporter.export(debtModel, rules);
    } finally {
      MyBatis.closeQuietly(session);
    }
  }

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

    Date updateDate = new Date(system2.now());
    DbSession session = dbClient.openSession(false);
    try {
      // Restore characteristics
      List<CharacteristicDto> allCharacteristicDtos = restoreCharacteristics(loadModelFromPlugin(DebtModelPluginRepository.DEFAULT_MODEL), updateDate, session);

      // Restore rules
      List<RuleDto> ruleDtos = dbClient.ruleDao().selectEnabledAndNonManual(session);
      if (!ruleDtos.isEmpty()) {

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

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

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

  private void resetRules(List<RuleDto> ruleDtos, List<RulesDefinition.Rule> rules, List<CharacteristicDto> allCharacteristicDtos, Date updateDate, DbSession session) {
    for (RuleDto rule : ruleDtos) {
      // Restore default debt definitions

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

      if (ruleDef != null) {
        String subCharacteristicKey = ruleDef.debtSubCharacteristic();
        CharacteristicDto subCharacteristicDto = characteristicByKey(subCharacteristicKey, allCharacteristicDtos, false);
        DebtRemediationFunction remediationFunction = ruleDef.debtRemediationFunction();
        boolean hasDebtDefinition = subCharacteristicDto != null && remediationFunction != null;

        rule.setDefaultSubCharacteristicId(hasDebtDefinition ? subCharacteristicDto.getId() : null);
        rule.setDefaultRemediationFunction(hasDebtDefinition ? remediationFunction.type().name() : null);
        rule.setDefaultRemediationCoefficient(hasDebtDefinition ? remediationFunction.coefficient() : null);
        rule.setDefaultRemediationOffset(hasDebtDefinition ? remediationFunction.offset() : null);
      }

      // Reset overridden debt definitions
      rule.setSubCharacteristicId(null);
      rule.setRemediationFunction(null);
      rule.setRemediationCoefficient(null);
      rule.setRemediationOffset(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 {
      List<CharacteristicDto> allCharacteristicDtos = restoreCharacteristics(characteristicsXMLImporter.importXML(xml), updateDate, session);
      restoreRules(allCharacteristicDtos, rules(languageKey, session), rulesXMLImporter.importXML(xml, validationMessages), validationMessages, updateDate, session);

      session.commit();
    } 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<CharacteristicDto> allCharacteristicDtos, List<RuleDto> rules, List<RuleDebt> ruleDebts,
                            ValidationMessages validationMessages, Date updateDate, DbSession session) {
    for (RuleDto rule : rules) {
      RuleDebt ruleDebt = ruleDebt(rule.getRepositoryKey(), rule.getRuleKey(), ruleDebts);
      String subCharacteristicKey = ruleDebt != null ? ruleDebt.subCharacteristicKey() : null;
      CharacteristicDto subCharacteristicDto = subCharacteristicKey != null ? characteristicByKey(ruleDebt.subCharacteristicKey(), allCharacteristicDtos, true) : null;
      ruleOperations.updateRule(rule, subCharacteristicDto,
        ruleDebt != null ? ruleDebt.function() : null,
        ruleDebt != null ? ruleDebt.coefficient() : null,
        ruleDebt != null ? ruleDebt.offset() : null, session);
      rule.setUpdatedAt(updateDate);
      ruleDebts.remove(ruleDebt);
    }

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

  @VisibleForTesting
  List<CharacteristicDto> restoreCharacteristics(DebtModel targetModel, Date updateDate, DbSession session) {
    List<CharacteristicDto> sourceCharacteristics = dbClient.debtCharacteristicDao().selectEnabledCharacteristics(session);

    List<CharacteristicDto> result = newArrayList();

    // Create new characteristics
    for (DebtCharacteristic characteristic : targetModel.rootCharacteristics()) {
      CharacteristicDto rootCharacteristicDto = restoreCharacteristic(characteristic, null, sourceCharacteristics, updateDate, session);
      result.add(rootCharacteristicDto);
      for (DebtCharacteristic subCharacteristic : targetModel.subCharacteristics(characteristic.key())) {
        result.add(restoreCharacteristic(subCharacteristic, rootCharacteristicDto.getId(), sourceCharacteristics, updateDate, session));
      }
    }
    // Disable no more existing characteristics
    for (CharacteristicDto sourceCharacteristic : sourceCharacteristics) {
      if (targetModel.characteristicByKey(sourceCharacteristic.getKey()) == null) {
        debtModelOperations.delete(sourceCharacteristic, updateDate, session);
      }
    }
    return result;
  }

  private CharacteristicDto restoreCharacteristic(DebtCharacteristic targetCharacteristic, @Nullable Integer parentId, List<CharacteristicDto> sourceCharacteristics,
                                                  Date updateDate, DbSession session) {
    CharacteristicDto sourceCharacteristic = characteristicByKey(targetCharacteristic.key(), sourceCharacteristics, false);
    if (sourceCharacteristic == null) {
      CharacteristicDto newCharacteristic = toDto(targetCharacteristic, parentId).setCreatedAt(updateDate);
      dbClient.debtCharacteristicDao().insert(newCharacteristic, session);
      return newCharacteristic;
    } else {
      // Update only if modifications
      if (ObjectUtils.notEqual(sourceCharacteristic.getName(), targetCharacteristic.name()) ||
        ObjectUtils.notEqual(sourceCharacteristic.getOrder(), targetCharacteristic.order()) ||
        ObjectUtils.notEqual(sourceCharacteristic.getParentId(), parentId)) {
        sourceCharacteristic.setName(targetCharacteristic.name());
        sourceCharacteristic.setOrder(targetCharacteristic.order());
        sourceCharacteristic.setParentId(parentId);
        sourceCharacteristic.setUpdatedAt(updateDate);
        dbClient.debtCharacteristicDao().update(sourceCharacteristic, session);
      }
      return sourceCharacteristic;
    }
  }

  private DebtModel loadModelFromPlugin(String pluginKey) {
    Reader xmlFileReader = null;
    try {
      xmlFileReader = debtModelPluginRepository.createReaderForXMLFile(pluginKey);
      return characteristicsXMLImporter.importXML(xmlFileReader);
    } finally {
      IOUtils.closeQuietly(xmlFileReader);
    }
  }

  private List<RuleDto> rules(@Nullable final String languageKey, DbSession session) {
    List<RuleDto> rules = dbClient.ruleDao().selectEnabledAndNonManual(session);
    if (languageKey == null) {
      return rules;
    } else {
      return newArrayList(Iterables.filter(rules, new Predicate<RuleDto>() {
        @Override
        public boolean apply(@Nullable RuleDto input) {
          return input != null && languageKey.equals(input.getLanguage());
        }
      }));
    }
  }

  @CheckForNull
  private static RuleDebt ruleDebt(final String repo, final String key, List<RuleDebt> ruleDebts) {
    if (ruleDebts.isEmpty()) {
      return null;
    }
    return Iterables.find(ruleDebts, new Predicate<RuleDebt>() {
      @Override
      public boolean apply(@Nullable RuleDebt input) {
        return input != null && repo.equals(input.ruleKey().repository()) && key.equals(input.ruleKey().rule());
      }
    }, null);
  }

  private static RuleDto rule(final Integer id, List<RuleDto> rules) {
    return Iterables.find(rules, new Predicate<RuleDto>() {
      @Override
      public boolean apply(@Nullable RuleDto input) {
        return input != null && id.equals(input.getId());
      }
    });
  }

  @CheckForNull
  private static RulesDefinition.Rule ruleDef(final String repo, final String key, List<RulesDefinition.Rule> rules) {
    return Iterables.find(rules, new Predicate<RulesDefinition.Rule>() {
      @Override
      public boolean apply(@Nullable RulesDefinition.Rule input) {
        return input != null && repo.equals(input.repository().key()) && key.equals(input.key());
      }
    }, null);
  }

  private static CharacteristicDto characteristicByKey(@Nullable final String key, List<CharacteristicDto> characteristicDtos, boolean failIfNotFound) {
    if (key == null) {
      return null;
    }
    CharacteristicDto dto = Iterables.find(characteristicDtos, new Predicate<CharacteristicDto>() {
      @Override
      public boolean apply(@Nullable CharacteristicDto input) {
        return input != null && key.equals(input.getKey());
      }
    }, null);
    if (dto == null && failIfNotFound) {
      throw new NotFoundException(String.format("Characteristic '%s' has not been found", key));
    }
    return dto;
  }

  private static List<CharacteristicDto> subCharacteristics(final Integer parentId, List<CharacteristicDto> allCharacteristics) {
    return newArrayList(Iterables.filter(allCharacteristics, new Predicate<CharacteristicDto>() {
      @Override
      public boolean apply(@Nullable CharacteristicDto input) {
        return input != null && parentId.equals(input.getParentId());
      }
    }));
  }

  @CheckForNull
  private static RuleDebt toRuleDebt(RuleDto rule, DebtModel debtModel) {
    RuleDebt ruleDebt = new RuleDebt().setRuleKey(RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()));
    Integer effectiveSubCharacteristicId = rule.getSubCharacteristicId() != null ? rule.getSubCharacteristicId() : rule.getDefaultSubCharacteristicId();
    DebtCharacteristic subCharacteristic = (effectiveSubCharacteristicId != null && !RuleDto.DISABLED_CHARACTERISTIC_ID.equals(effectiveSubCharacteristicId)) ?
      debtModel.characteristicById(effectiveSubCharacteristicId) : null;
    if (subCharacteristic != null) {
      ruleDebt.setSubCharacteristicKey(subCharacteristic.key());

      String overriddenFunction = rule.getRemediationFunction();
      String defaultFunction = rule.getDefaultRemediationFunction();
      if (overriddenFunction != null) {
        ruleDebt.setFunction(overriddenFunction);
        ruleDebt.setCoefficient(rule.getRemediationCoefficient());
        ruleDebt.setOffset(rule.getRemediationOffset());
        return ruleDebt;
      } else if (defaultFunction != null) {
        ruleDebt.setFunction(defaultFunction);
        ruleDebt.setCoefficient(rule.getDefaultRemediationCoefficient());
        ruleDebt.setOffset(rule.getDefaultRemediationOffset());
        return ruleDebt;
      }
    }
    return null;
  }

  private static CharacteristicDto toDto(DebtCharacteristic characteristic, @Nullable Integer parentId) {
    return new CharacteristicDto()
      .setKey(characteristic.key())
      .setName(characteristic.name())
      .setOrder(characteristic.order())
      .setParentId(parentId)
      .setEnabled(true)
      .setCreatedAt(((DefaultDebtCharacteristic) characteristic).createdAt())
      .setUpdatedAt(((DefaultDebtCharacteristic) characteristic).updatedAt());
  }

  private static DebtCharacteristic toDebtCharacteristic(CharacteristicDto characteristic) {
    return new DefaultDebtCharacteristic()
      .setId(characteristic.getId())
      .setKey(characteristic.getKey())
      .setName(characteristic.getName())
      .setOrder(characteristic.getOrder())
      .setParentId(characteristic.getParentId())
      .setCreatedAt(characteristic.getCreatedAt())
      .setUpdatedAt(characteristic.getUpdatedAt());
  }

  private void checkPermission() {
    UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
  }

}
TOP

Related Classes of org.sonar.server.debt.DebtModelBackup

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.