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 */
020 package org.sonar.server.configuration;
021
022 import com.thoughtworks.xstream.XStream;
023 import com.thoughtworks.xstream.converters.Converter;
024 import com.thoughtworks.xstream.converters.MarshallingContext;
025 import com.thoughtworks.xstream.converters.UnmarshallingContext;
026 import com.thoughtworks.xstream.io.HierarchicalStreamReader;
027 import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
028 import org.apache.commons.collections.CollectionUtils;
029 import org.slf4j.LoggerFactory;
030 import org.sonar.api.database.DatabaseSession;
031 import org.sonar.api.measures.Metric;
032 import org.sonar.api.profiles.Alert;
033 import org.sonar.api.profiles.RulesProfile;
034 import org.sonar.api.rules.*;
035 import org.sonar.jpa.dao.RulesDao;
036
037 import java.util.*;
038
039 public 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 }