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    }