001/* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020package org.sonar.server.configuration; 021 022import com.thoughtworks.xstream.XStream; 023import com.thoughtworks.xstream.converters.Converter; 024import com.thoughtworks.xstream.converters.MarshallingContext; 025import com.thoughtworks.xstream.converters.UnmarshallingContext; 026import com.thoughtworks.xstream.io.HierarchicalStreamReader; 027import com.thoughtworks.xstream.io.HierarchicalStreamWriter; 028import org.apache.commons.collections.CollectionUtils; 029import org.slf4j.LoggerFactory; 030import org.sonar.api.database.DatabaseSession; 031import org.sonar.api.measures.Metric; 032import org.sonar.api.profiles.Alert; 033import org.sonar.api.profiles.RulesProfile; 034import org.sonar.api.rules.*; 035import org.sonar.jpa.dao.RulesDao; 036 037import java.util.*; 038 039public class ProfilesBackup implements Backupable { 040 041 private Collection<RulesProfile> profiles; 042 private DatabaseSession session; 043 044 public ProfilesBackup(DatabaseSession session) { 045 this.session = session; 046 } 047 048 /** 049 * for unit tests 050 */ 051 ProfilesBackup(Collection<RulesProfile> profiles) { 052 this.profiles = profiles; 053 } 054 055 public void configure(XStream xStream) { 056 xStream.alias("profile", RulesProfile.class); 057 xStream.alias("alert", Alert.class); 058 xStream.alias("active-rule", ActiveRule.class); 059 xStream.aliasField("active-rules", RulesProfile.class, "activeRules"); 060 xStream.aliasField("default-profile", RulesProfile.class, "defaultProfile"); 061 xStream.omitField(RulesProfile.class, "id"); 062 xStream.omitField(RulesProfile.class, "projects"); 063 xStream.registerConverter(getActiveRuleConverter()); 064 xStream.registerConverter(getAlertsConverter()); 065 } 066 067 public void exportXml(SonarConfig sonarConfig) { 068 this.profiles = (this.profiles == null ? session.getResults(RulesProfile.class) : this.profiles); 069 // the profiles objects must be cloned to avoid issues CGLib 070 List<RulesProfile> cloned = new ArrayList<RulesProfile>(); 071 for (RulesProfile profile : this.profiles) { 072 cloned.add((RulesProfile) profile.clone()); 073 } 074 075 sonarConfig.setProfiles(cloned); 076 } 077 078 public void importXml(SonarConfig sonarConfig) { 079 if (CollectionUtils.isNotEmpty(sonarConfig.getProfiles())) { 080 LoggerFactory.getLogger(getClass()).info("Delete profiles"); 081 ProfilesManager profilesManager = new ProfilesManager(session, null); 082 profilesManager.deleteAllProfiles(); 083 084 RulesDao rulesDao = new RulesDao(session); 085 for (RulesProfile profile : sonarConfig.getProfiles()) { 086 LoggerFactory.getLogger(getClass()).info("Restore profile " + profile.getName()); 087 importProfile(rulesDao, profile); 088 } 089 } 090 } 091 092 public void importProfile(RulesDao rulesDao, RulesProfile toImport) { 093 if (toImport.getEnabled() == null) { 094 // backward-compatibility with versions < 2.6. The field "enabled" did not exist. Default value is true. 095 toImport.setEnabled(true); 096 } 097 if (toImport.getVersion() == 0) { 098 // backward-compatibility with versions < 2.9. The field "version" did not exist. Default value is 1. 099 toImport.setVersion(1); 100 } 101 if (toImport.getUsed() == null) { 102 // backward-compatibility with versions < 2.9. The field "used_profile" did not exist. Default value is false. 103 toImport.setUsed(false); 104 } 105 importActiveRules(rulesDao, toImport); 106 importAlerts(toImport); 107 session.save(toImport); 108 } 109 110 private void importAlerts(RulesProfile profile) { 111 if (profile.getAlerts() != null) { 112 for (Iterator<Alert> ia = profile.getAlerts().iterator(); ia.hasNext();) { 113 Alert alert = ia.next(); 114 Metric unMarshalledMetric = alert.getMetric(); 115 String validKey = unMarshalledMetric.getKey(); 116 Metric matchingMetricInDb = session.getSingleResult(Metric.class, "key", validKey); 117 if (matchingMetricInDb == null) { 118 LoggerFactory.getLogger(getClass()).error("Unable to find metric " + validKey); 119 ia.remove(); 120 continue; 121 } 122 alert.setMetric(matchingMetricInDb); 123 alert.setRulesProfile(profile); 124 } 125 } 126 } 127 128 private void importActiveRules(RulesDao rulesDao, RulesProfile profile) { 129 for (Iterator<ActiveRule> iar = profile.getActiveRules(true).iterator(); iar.hasNext();) { 130 ActiveRule activeRule = iar.next(); 131 Rule unMarshalledRule = activeRule.getRule(); 132 Rule matchingRuleInDb = rulesDao.getRuleByKey(unMarshalledRule.getRepositoryKey(), unMarshalledRule.getKey()); 133 if (matchingRuleInDb == null) { 134 LoggerFactory.getLogger(getClass()).error( 135 "Unable to find active rule " + unMarshalledRule.getRepositoryKey() + ":" + unMarshalledRule.getKey()); 136 iar.remove(); 137 continue; 138 } 139 activeRule.setRule(matchingRuleInDb); 140 activeRule.setRulesProfile(profile); 141 activeRule.getActiveRuleParams(); 142 for (Iterator<ActiveRuleParam> irp = activeRule.getActiveRuleParams().iterator(); irp.hasNext();) { 143 ActiveRuleParam activeRuleParam = irp.next(); 144 RuleParam unMarshalledRP = activeRuleParam.getRuleParam(); 145 RuleParam matchingRPInDb = rulesDao.getRuleParam(matchingRuleInDb, unMarshalledRP.getKey()); 146 if (matchingRPInDb == null) { 147 LoggerFactory.getLogger(getClass()).error("Unable to find active rule parameter " + unMarshalledRP.getKey()); 148 irp.remove(); 149 continue; 150 } 151 activeRuleParam.setActiveRule(activeRule); 152 activeRuleParam.setRuleParam(matchingRPInDb); 153 } 154 } 155 } 156 157 private Converter getAlertsConverter() { 158 return new Converter() { 159 160 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { 161 Alert alert = (Alert) source; 162 writeNode(writer, "operator", alert.getOperator()); 163 writeNode(writer, "value-error", alert.getValueError()); 164 writeNode(writer, "value-warning", alert.getValueWarning()); 165 writeNode(writer, "metric-key", alert.getMetric().getKey()); 166 } 167 168 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { 169 Map<String, String> values = readNode(reader); 170 return new Alert(null, new Metric(values.get("metric-key")), values.get("operator"), values.get("value-error"), 171 values.get("value-warning")); 172 } 173 174 public boolean canConvert(Class type) { 175 return type.equals(Alert.class); 176 } 177 }; 178 } 179 180 private Converter getActiveRuleConverter() { 181 return new Converter() { 182 183 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { 184 ActiveRule rule = (ActiveRule) source; 185 writeNode(writer, "key", rule.getRule().getKey()); 186 writeNode(writer, "plugin", rule.getRule().getRepositoryKey()); 187 writeNode(writer, "level", rule.getSeverity().name()); 188 writeNode(writer, "inheritance", rule.getInheritance()); 189 190 if (!rule.getActiveRuleParams().isEmpty()) { 191 writer.startNode("params"); 192 for (ActiveRuleParam activeRuleParam : rule.getActiveRuleParams()) { 193 writer.startNode("param"); 194 writeNode(writer, "key", activeRuleParam.getRuleParam().getKey()); 195 writeNode(writer, "value", activeRuleParam.getValue()); 196 writer.endNode(); 197 } 198 writer.endNode(); 199 } 200 } 201 202 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { 203 Map<String, String> valuesRule = new HashMap<String, String>(); 204 List<ActiveRuleParam> params = new ArrayList<ActiveRuleParam>(); 205 while (reader.hasMoreChildren()) { 206 reader.moveDown(); 207 valuesRule.put(reader.getNodeName(), reader.getValue()); 208 if ("params".equals(reader.getNodeName())) { 209 while (reader.hasMoreChildren()) { 210 reader.moveDown(); 211 Map<String, String> valuesParam = readNode(reader); 212 ActiveRuleParam activeRuleParam = new ActiveRuleParam(null, new RuleParam(null, valuesParam.get("key"), null, null), 213 valuesParam.get("value")); 214 params.add(activeRuleParam); 215 reader.moveUp(); 216 } 217 } 218 reader.moveUp(); 219 } 220 221 ActiveRule activeRule = new ActiveRule(null, new Rule(valuesRule.get("plugin"), valuesRule.get("key")), RulePriority 222 .valueOf(valuesRule.get("level"))); 223 activeRule.setActiveRuleParams(params); 224 if (valuesRule.containsKey("inheritance")) { 225 activeRule.setInheritance(valuesRule.get("inheritance")); 226 } 227 return activeRule; 228 } 229 230 public boolean canConvert(Class type) { 231 return type.equals(ActiveRule.class); 232 } 233 }; 234 } 235 236 private void writeNode(HierarchicalStreamWriter writer, String name, String value) { 237 if (value != null) { 238 writer.startNode(name); 239 writer.setValue(value); 240 writer.endNode(); 241 } 242 } 243 244 private Map<String, String> readNode(HierarchicalStreamReader reader) { 245 Map<String, String> values = new HashMap<String, String>(); 246 while (reader.hasMoreChildren()) { 247 reader.moveDown(); 248 values.put(reader.getNodeName(), reader.getValue()); 249 reader.moveUp(); 250 } 251 return values; 252 } 253 254}