Java tutorial
/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info 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.qualityprofile; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.io.Reader; import java.io.Writer; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import javax.annotation.Nullable; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.CompareToBuilder; import org.codehaus.staxmate.SMInputFactory; import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; import org.sonar.api.rule.RuleKey; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.text.XmlWriter; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleParamDto; import org.sonar.db.qualityprofile.QualityProfileDto; import static com.google.common.base.Preconditions.checkArgument; @ServerSide public class QProfileBackuperImpl implements QProfileBackuper { private static final Joiner RULE_KEY_JOINER = Joiner.on(", ").skipNulls(); private static final String ATTRIBUTE_PROFILE = "profile"; private static final String ATTRIBUTE_NAME = "name"; private static final String ATTRIBUTE_LANGUAGE = "language"; private static final String ATTRIBUTE_RULES = "rules"; private static final String ATTRIBUTE_RULE = "rule"; private static final String ATTRIBUTE_REPOSITORY_KEY = "repositoryKey"; private static final String ATTRIBUTE_KEY = "key"; private static final String ATTRIBUTE_PRIORITY = "priority"; private static final String ATTRIBUTE_PARAMETERS = "parameters"; private static final String ATTRIBUTE_PARAMETER = "parameter"; private static final String ATTRIBUTE_PARAMETER_KEY = "key"; private static final String ATTRIBUTE_PARAMETER_VALUE = "value"; private final DbClient db; private final QProfileReset profileReset; private final QProfileFactory profileFactory; public QProfileBackuperImpl(DbClient db, QProfileReset profileReset, QProfileFactory profileFactory) { this.db = db; this.profileReset = profileReset; this.profileFactory = profileFactory; } @Override public void backup(DbSession dbSession, QualityProfileDto profileDto, Writer writer) { List<ActiveRuleDto> activeRules = db.activeRuleDao().selectByProfileKey(dbSession, profileDto.getKey()); activeRules.sort(BackupActiveRuleComparator.INSTANCE); writeXml(dbSession, writer, profileDto, activeRules.iterator()); } private void writeXml(DbSession dbSession, Writer writer, QualityProfileDto profile, Iterator<ActiveRuleDto> activeRules) { XmlWriter xml = XmlWriter.of(writer).declaration(); xml.begin(ATTRIBUTE_PROFILE); xml.prop(ATTRIBUTE_NAME, profile.getName()); xml.prop(ATTRIBUTE_LANGUAGE, profile.getLanguage()); xml.begin(ATTRIBUTE_RULES); while (activeRules.hasNext()) { ActiveRuleDto activeRule = activeRules.next(); xml.begin(ATTRIBUTE_RULE); xml.prop(ATTRIBUTE_REPOSITORY_KEY, activeRule.getKey().ruleKey().repository()); xml.prop(ATTRIBUTE_KEY, activeRule.getKey().ruleKey().rule()); xml.prop(ATTRIBUTE_PRIORITY, activeRule.getSeverityString()); xml.begin(ATTRIBUTE_PARAMETERS); for (ActiveRuleParamDto param : db.activeRuleDao().selectParamsByActiveRuleId(dbSession, activeRule.getId())) { xml.begin(ATTRIBUTE_PARAMETER).prop(ATTRIBUTE_PARAMETER_KEY, param.getKey()) .prop(ATTRIBUTE_PARAMETER_VALUE, param.getValue()).end(); } xml.end(ATTRIBUTE_PARAMETERS); xml.end(ATTRIBUTE_RULE); } xml.end(ATTRIBUTE_RULES).end(ATTRIBUTE_PROFILE).close(); } @Override public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, OrganizationDto organization, @Nullable String overriddenProfileName) { return restore(dbSession, backup, nameInBackup -> { QProfileName targetName = nameInBackup; if (overriddenProfileName != null) { targetName = new QProfileName(nameInBackup.getLanguage(), overriddenProfileName); } return profileFactory.getOrCreate(dbSession, organization, targetName); }); } @Override public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, QualityProfileDto profile) { return restore(dbSession, backup, nameInBackup -> { checkArgument(profile.getLanguage().equals(nameInBackup.getLanguage()), "Can't restore %s backup on %s profile with key [%s]. Languages are different.", nameInBackup.getLanguage(), profile.getLanguage(), profile.getKey()); return profile; }); } private QProfileRestoreSummary restore(DbSession dbSession, Reader backup, Function<QProfileName, QualityProfileDto> profileLoader) { try { String profileLang = null; String profileName = null; List<RuleActivation> ruleActivations = Lists.newArrayList(); SMInputFactory inputFactory = initStax(); SMHierarchicCursor rootC = inputFactory.rootElementCursor(backup); rootC.advance(); // <profile> if (!"profile".equals(rootC.getLocalName())) { throw new IllegalArgumentException("Backup XML is not valid. Root element must be <profile>."); } SMInputCursor cursor = rootC.childElementCursor(); while (cursor.getNext() != null) { String nodeName = cursor.getLocalName(); if (StringUtils.equals(ATTRIBUTE_NAME, nodeName)) { profileName = StringUtils.trim(cursor.collectDescendantText(false)); } else if (StringUtils.equals(ATTRIBUTE_LANGUAGE, nodeName)) { profileLang = StringUtils.trim(cursor.collectDescendantText(false)); } else if (StringUtils.equals(ATTRIBUTE_RULES, nodeName)) { SMInputCursor rulesCursor = cursor.childElementCursor("rule"); ruleActivations = parseRuleActivations(rulesCursor); } } QProfileName targetName = new QProfileName(profileLang, profileName); QualityProfileDto targetProfile = profileLoader.apply(targetName); BulkChangeResult changes = profileReset.reset(dbSession, targetProfile, ruleActivations); return new QProfileRestoreSummary(targetProfile, changes); } catch (XMLStreamException e) { throw new IllegalStateException("Fail to restore Quality profile backup", e); } } private static List<RuleActivation> parseRuleActivations(SMInputCursor rulesCursor) throws XMLStreamException { List<RuleActivation> activations = Lists.newArrayList(); Set<RuleKey> activatedKeys = Sets.newHashSet(); List<RuleKey> duplicatedKeys = Lists.newArrayList(); while (rulesCursor.getNext() != null) { SMInputCursor ruleCursor = rulesCursor.childElementCursor(); String repositoryKey = null; String key = null; String severity = null; Map<String, String> parameters = Maps.newHashMap(); while (ruleCursor.getNext() != null) { String nodeName = ruleCursor.getLocalName(); if (StringUtils.equals(ATTRIBUTE_REPOSITORY_KEY, nodeName)) { repositoryKey = StringUtils.trim(ruleCursor.collectDescendantText(false)); } else if (StringUtils.equals(ATTRIBUTE_KEY, nodeName)) { key = StringUtils.trim(ruleCursor.collectDescendantText(false)); } else if (StringUtils.equals(ATTRIBUTE_PRIORITY, nodeName)) { severity = StringUtils.trim(ruleCursor.collectDescendantText(false)); } else if (StringUtils.equals(ATTRIBUTE_PARAMETERS, nodeName)) { SMInputCursor propsCursor = ruleCursor.childElementCursor(ATTRIBUTE_PARAMETER); readParameters(propsCursor, parameters); } } RuleKey ruleKey = RuleKey.of(repositoryKey, key); if (activatedKeys.contains(ruleKey)) { duplicatedKeys.add(ruleKey); } activatedKeys.add(ruleKey); RuleActivation activation = new RuleActivation(ruleKey); activation.setSeverity(severity); activation.setParameters(parameters); activations.add(activation); } if (!duplicatedKeys.isEmpty()) { throw new IllegalArgumentException( "The quality profile cannot be restored as it contains duplicates for the following rules: " + RULE_KEY_JOINER.join(duplicatedKeys)); } return activations; } private static void readParameters(SMInputCursor propsCursor, Map<String, String> parameters) throws XMLStreamException { while (propsCursor.getNext() != null) { SMInputCursor propCursor = propsCursor.childElementCursor(); String key = null; String value = null; while (propCursor.getNext() != null) { String nodeName = propCursor.getLocalName(); if (StringUtils.equals(ATTRIBUTE_PARAMETER_KEY, nodeName)) { key = StringUtils.trim(propCursor.collectDescendantText(false)); } else if (StringUtils.equals(ATTRIBUTE_PARAMETER_VALUE, nodeName)) { value = StringUtils.trim(propCursor.collectDescendantText(false)); } } if (key != null) { parameters.put(key, value); } } } private static SMInputFactory initStax() { XMLInputFactory xmlFactory = XMLInputFactory.newInstance(); xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE); // just so it won't try to load DTD in if there's DOCTYPE xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE); return new SMInputFactory(xmlFactory); } private enum BackupActiveRuleComparator implements Comparator<ActiveRuleDto> { INSTANCE; @Override public int compare(ActiveRuleDto o1, ActiveRuleDto o2) { return new CompareToBuilder() .append(o1.getKey().ruleKey().repository(), o2.getKey().ruleKey().repository()) .append(o1.getKey().ruleKey().rule(), o2.getKey().ruleKey().rule()).toComparison(); } } }