001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2011 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 ProfilesManager profilesManager = new ProfilesManager(session, null);
081 profilesManager.deleteAllProfiles();
082
083 RulesDao rulesDao = new RulesDao(session);
084 for (RulesProfile profile : sonarConfig.getProfiles()) {
085 importProfile(rulesDao, profile);
086 }
087 }
088 }
089
090 public void importProfile(RulesDao rulesDao, RulesProfile toImport) {
091 if (toImport.getEnabled() == null) {
092 // backward-compatibility with versions < 2.6. The field "enabled" did not exist. Default value is true.
093 toImport.setEnabled(true);
094 }
095 if (toImport.getVersion() == 0) {
096 // backward-compatibility with versions < 2.9. The field "version" did not exist. Default value is 1.
097 toImport.setVersion(1);
098 }
099 if (toImport.getUsed() == null) {
100 // backward-compatibility with versions < 2.9. The field "used_profile" did not exist. Default value is false.
101 toImport.setUsed(false);
102 }
103 importActiveRules(rulesDao, toImport);
104 importAlerts(toImport);
105 session.save(toImport);
106 }
107
108 private void importAlerts(RulesProfile profile) {
109 if (profile.getAlerts() != null) {
110 for (Iterator<Alert> ia = profile.getAlerts().iterator(); ia.hasNext();) {
111 Alert alert = ia.next();
112 Metric unMarshalledMetric = alert.getMetric();
113 String validKey = unMarshalledMetric.getKey();
114 Metric matchingMetricInDb = session.getSingleResult(Metric.class, "key", validKey);
115 if (matchingMetricInDb == null) {
116 LoggerFactory.getLogger(getClass()).error("Unable to find metric " + validKey);
117 ia.remove();
118 continue;
119 }
120 alert.setMetric(matchingMetricInDb);
121 alert.setRulesProfile(profile);
122 }
123 }
124 }
125
126 private void importActiveRules(RulesDao rulesDao, RulesProfile profile) {
127 for (Iterator<ActiveRule> iar = profile.getActiveRules(true).iterator(); iar.hasNext();) {
128 ActiveRule activeRule = iar.next();
129 Rule unMarshalledRule = activeRule.getRule();
130 Rule matchingRuleInDb = rulesDao.getRuleByKey(unMarshalledRule.getRepositoryKey(), unMarshalledRule.getKey());
131 if (matchingRuleInDb == null) {
132 LoggerFactory.getLogger(getClass()).error(
133 "Unable to find active rule " + unMarshalledRule.getRepositoryKey() + ":" + unMarshalledRule.getKey());
134 iar.remove();
135 continue;
136 }
137 activeRule.setRule(matchingRuleInDb);
138 activeRule.setRulesProfile(profile);
139 activeRule.getActiveRuleParams();
140 for (Iterator<ActiveRuleParam> irp = activeRule.getActiveRuleParams().iterator(); irp.hasNext();) {
141 ActiveRuleParam activeRuleParam = irp.next();
142 RuleParam unMarshalledRP = activeRuleParam.getRuleParam();
143 RuleParam matchingRPInDb = rulesDao.getRuleParam(matchingRuleInDb, unMarshalledRP.getKey());
144 if (matchingRPInDb == null) {
145 LoggerFactory.getLogger(getClass()).error("Unable to find active rule parameter " + unMarshalledRP.getKey());
146 irp.remove();
147 continue;
148 }
149 activeRuleParam.setActiveRule(activeRule);
150 activeRuleParam.setRuleParam(matchingRPInDb);
151 }
152 }
153 }
154
155 private Converter getAlertsConverter() {
156 return new Converter() {
157
158 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
159 Alert alert = (Alert) source;
160 writeNode(writer, "operator", alert.getOperator());
161 writeNode(writer, "value-error", alert.getValueError());
162 writeNode(writer, "value-warning", alert.getValueWarning());
163 writeNode(writer, "metric-key", alert.getMetric().getKey());
164 }
165
166 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
167 Map<String, String> values = readNode(reader);
168 return new Alert(null, new Metric(values.get("metric-key")), values.get("operator"), values.get("value-error"),
169 values.get("value-warning"));
170 }
171
172 public boolean canConvert(Class type) {
173 return type.equals(Alert.class);
174 }
175 };
176 }
177
178 private Converter getActiveRuleConverter() {
179 return new Converter() {
180
181 public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
182 ActiveRule rule = (ActiveRule) source;
183 writeNode(writer, "key", rule.getRule().getKey());
184 writeNode(writer, "plugin", rule.getRule().getRepositoryKey());
185 writeNode(writer, "level", rule.getSeverity().name());
186 writeNode(writer, "inheritance", rule.getInheritance());
187
188 if (!rule.getActiveRuleParams().isEmpty()) {
189 writer.startNode("params");
190 for (ActiveRuleParam activeRuleParam : rule.getActiveRuleParams()) {
191 writer.startNode("param");
192 writeNode(writer, "key", activeRuleParam.getRuleParam().getKey());
193 writeNode(writer, "value", activeRuleParam.getValue());
194 writer.endNode();
195 }
196 writer.endNode();
197 }
198 }
199
200 public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
201 Map<String, String> valuesRule = new HashMap<String, String>();
202 List<ActiveRuleParam> params = new ArrayList<ActiveRuleParam>();
203 while (reader.hasMoreChildren()) {
204 reader.moveDown();
205 valuesRule.put(reader.getNodeName(), reader.getValue());
206 if ("params".equals(reader.getNodeName())) {
207 while (reader.hasMoreChildren()) {
208 reader.moveDown();
209 Map<String, String> valuesParam = readNode(reader);
210 ActiveRuleParam activeRuleParam = new ActiveRuleParam(null, new RuleParam(null, valuesParam.get("key"), null, null),
211 valuesParam.get("value"));
212 params.add(activeRuleParam);
213 reader.moveUp();
214 }
215 }
216 reader.moveUp();
217 }
218
219 ActiveRule activeRule = new ActiveRule(null, new Rule(valuesRule.get("plugin"), valuesRule.get("key")), RulePriority
220 .valueOf(valuesRule.get("level")));
221 activeRule.setActiveRuleParams(params);
222 if (valuesRule.containsKey("inheritance")) {
223 activeRule.setInheritance(valuesRule.get("inheritance"));
224 }
225 return activeRule;
226 }
227
228 public boolean canConvert(Class type) {
229 return type.equals(ActiveRule.class);
230 }
231 };
232 }
233
234 private void writeNode(HierarchicalStreamWriter writer, String name, String value) {
235 if (value != null) {
236 writer.startNode(name);
237 writer.setValue(value);
238 writer.endNode();
239 }
240 }
241
242 private Map<String, String> readNode(HierarchicalStreamReader reader) {
243 Map<String, String> values = new HashMap<String, String>();
244 while (reader.hasMoreChildren()) {
245 reader.moveDown();
246 values.put(reader.getNodeName(), reader.getValue());
247 reader.moveUp();
248 }
249 return values;
250 }
251
252 }