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    }