]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1987: Backup/restore user rules
authorGodin <mandrikov@gmail.com>
Wed, 8 Dec 2010 14:53:08 +0000 (14:53 +0000)
committerGodin <mandrikov@gmail.com>
Wed, 8 Dec 2010 14:53:08 +0000 (14:53 +0000)
sonar-server/src/main/java/org/sonar/server/configuration/Backup.java
sonar-server/src/main/java/org/sonar/server/configuration/ProfilesBackup.java
sonar-server/src/main/java/org/sonar/server/configuration/RulesBackup.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/configuration/SonarConfig.java
sonar-server/src/test/java/org/sonar/server/configuration/BackupTest.java
sonar-server/src/test/java/org/sonar/server/configuration/RulesBackupTest.java [new file with mode: 0644]

index a8ed347761e8ceca39d438675736d51c2f80bc43..eba4e48108305c6910dc47d872a09de4443d143e 100644 (file)
@@ -56,6 +56,8 @@ public class Backup {
 
     backupables.add(new MetricsBackup(session));
     backupables.add(new PropertiesBackup(session));
+    // Note that order is important, because profile can have reference to rule
+    backupables.add(new RulesBackup(session));
     backupables.add(new ProfilesBackup(session));
   }
 
@@ -149,11 +151,9 @@ public class Backup {
               protected void writeText(QuickWriter writer, String text) {
                 writer.write("<![CDATA[");
                 /*
-                 * See http://jira.codehaus.org/browse/SONAR-1605
-                 * According to XML specification ( http://www.w3.org/TR/REC-xml/#sec-cdata-sect )
-                 * CData section may contain everything except of sequence ']]>'
-                 * so we will split all occurrences of this sequence into two CDATA
-                 * first one would contain ']]' and second '>'
+                 * See http://jira.codehaus.org/browse/SONAR-1605 According to XML specification (
+                 * http://www.w3.org/TR/REC-xml/#sec-cdata-sect ) CData section may contain everything except of sequence ']]>' so we will
+                 * split all occurrences of this sequence into two CDATA first one would contain ']]' and second '>'
                  */
                 text = StringUtils.replace(text, "]]>", "]]]]><![CDATA[>");
                 writer.write(text);
index 16f84aad30624f6c0f243e35c2d644d1e1cd696f..3149e62923c01ac6c92701cc4cf550312314982f 100644 (file)
@@ -111,9 +111,10 @@ public class ProfilesBackup implements Backupable {
     for (Iterator<ActiveRule> iar = profile.getActiveRules().iterator(); iar.hasNext();) {
       ActiveRule activeRule = iar.next();
       Rule unMarshalledRule = activeRule.getRule();
-      Rule matchingRuleInDb = rulesDao.getRuleByKey(unMarshalledRule.getPluginName(), unMarshalledRule.getKey());
+      Rule matchingRuleInDb = rulesDao.getRuleByKey(unMarshalledRule.getRepositoryKey(), unMarshalledRule.getKey());
       if (matchingRuleInDb == null) {
-        LoggerFactory.getLogger(getClass()).error("Unable to find active rule " + unMarshalledRule.getPluginName() + ":" + unMarshalledRule.getKey());
+        LoggerFactory.getLogger(getClass()).error(
+            "Unable to find active rule " + unMarshalledRule.getRepositoryKey() + ":" + unMarshalledRule.getKey());
         iar.remove();
         continue;
       }
@@ -148,7 +149,8 @@ public class ProfilesBackup implements Backupable {
 
       public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
         Map<String, String> values = readNode(reader);
-        return new Alert(null, new Metric(values.get("metric-key")), values.get("operator"), values.get("value-error"), values.get("value-warning"));
+        return new Alert(null, new Metric(values.get("metric-key")), values.get("operator"), values.get("value-error"),
+            values.get("value-warning"));
       }
 
       public boolean canConvert(Class type) {
@@ -163,7 +165,7 @@ public class ProfilesBackup implements Backupable {
       public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
         ActiveRule rule = (ActiveRule) source;
         writeNode(writer, "key", rule.getRule().getKey());
-        writeNode(writer, "plugin", rule.getRule().getPluginName());
+        writeNode(writer, "plugin", rule.getRule().getRepositoryKey());
         writeNode(writer, "level", rule.getSeverity().name());
 
         if (!rule.getActiveRuleParams().isEmpty()) {
@@ -188,7 +190,8 @@ public class ProfilesBackup implements Backupable {
             while (reader.hasMoreChildren()) {
               reader.moveDown();
               Map<String, String> valuesParam = readNode(reader);
-              ActiveRuleParam activeRuleParam = new ActiveRuleParam(null, new RuleParam(null, valuesParam.get("key"), null, null), valuesParam.get("value"));
+              ActiveRuleParam activeRuleParam = new ActiveRuleParam(null, new RuleParam(null, valuesParam.get("key"), null, null),
+                  valuesParam.get("value"));
               params.add(activeRuleParam);
               reader.moveUp();
             }
diff --git a/sonar-server/src/main/java/org/sonar/server/configuration/RulesBackup.java b/sonar-server/src/main/java/org/sonar/server/configuration/RulesBackup.java
new file mode 100644 (file)
index 0000000..6238285
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.server.configuration;
+
+import com.google.common.collect.Lists;
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import org.apache.commons.collections.CollectionUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleParam;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.jpa.dao.RulesDao;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RulesBackup implements Backupable {
+  private Collection<Rule> rules;
+  private RulesDao rulesDao;
+  private DatabaseSession session;
+
+  public RulesBackup(DatabaseSession session) {
+    this.rulesDao = new RulesDao(session);
+    this.session = session;
+  }
+
+  /**
+   * For tests.
+   */
+  RulesBackup(Collection<Rule> rules) {
+    this.rules = rules;
+  }
+
+  public void exportXml(SonarConfig sonarConfig) {
+    if (rules == null) {
+      rules = getUserRules();
+    }
+    sonarConfig.setRules(rules);
+  }
+
+  public void importXml(SonarConfig sonarConfig) {
+    disableUserRules();
+    if (CollectionUtils.isNotEmpty(sonarConfig.getRules())) {
+      registerUserRules(sonarConfig.getRules());
+    }
+  }
+
+  private List<Rule> getUserRules() {
+    List<Rule> rules = rulesDao.getRules();
+    List<Rule> userRules = Lists.newArrayList();
+    for (Rule rule : rules) {
+      if (rule.getParent() != null) {
+        userRules.add(rule);
+      }
+    }
+    return userRules;
+  }
+
+  private void disableUserRules() {
+    for (Rule rule : getUserRules()) {
+      rule.setEnabled(false);
+      session.save(rule);
+    }
+  }
+
+  private void registerUserRules(Collection<Rule> rules) {
+    for (Rule rule : rules) {
+      Rule parent = rule.getParent();
+      Rule matchingParentRuleInDb = rulesDao.getRuleByKey(parent.getRepositoryKey(), parent.getKey());
+      if (matchingParentRuleInDb == null) {
+        LoggerFactory.getLogger(getClass()).error("Unable to find parent rule " + parent.getRepositoryKey() + ":" + parent.getKey());
+        continue;
+      }
+      rule.setParent(matchingParentRuleInDb);
+      Rule matchingRuleInDb = rulesDao.getRuleByKey(rule.getRepositoryKey(), rule.getKey());
+      if (matchingRuleInDb != null) {
+        matchingRuleInDb.setName(rule.getName());
+        matchingRuleInDb.setDescription(rule.getDescription());
+        matchingRuleInDb.setSeverity(rule.getSeverity());
+        matchingRuleInDb.setParams(rule.getParams());
+        matchingRuleInDb.setEnabled(true);
+        session.save(matchingRuleInDb);
+      } else {
+        rule.setEnabled(true);
+        session.save(rule);
+      }
+    }
+  }
+
+  public void configure(XStream xStream) {
+    xStream.alias("rule", Rule.class);
+    xStream.registerConverter(new Converter() {
+      public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
+        Rule rule = (Rule) source;
+        writeNode(writer, "parentRepositoryKey", rule.getParent().getRepositoryKey());
+        writeNode(writer, "parentKey", rule.getParent().getKey());
+        writeNode(writer, "repositoryKey", rule.getRepositoryKey());
+        writeNode(writer, "key", rule.getKey());
+        writeNode(writer, "level", rule.getSeverity().name());
+        writeNode(writer, "name", rule.getName());
+        writeNode(writer, "description", rule.getDescription());
+
+        if (!rule.getParams().isEmpty()) {
+          writer.startNode("params");
+          for (RuleParam ruleParam : rule.getParams()) {
+            writer.startNode("param");
+            writeNode(writer, "key", ruleParam.getKey());
+            writeNode(writer, "value", ruleParam.getDefaultValue());
+            writer.endNode();
+          }
+          writer.endNode();
+        }
+      }
+
+      public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
+        Rule rule = Rule.create();
+
+        Map<String, String> valuesRule = new HashMap<String, String>();
+        while (reader.hasMoreChildren()) {
+          reader.moveDown();
+          valuesRule.put(reader.getNodeName(), reader.getValue());
+          if (reader.getNodeName().equals("params")) {
+            while (reader.hasMoreChildren()) {
+              reader.moveDown();
+              Map<String, String> valuesParam = readNode(reader);
+              rule.createParameter()
+                  .setKey(valuesParam.get("key"))
+                  .setDefaultValue(valuesParam.get("value"));
+              reader.moveUp();
+            }
+          }
+          reader.moveUp();
+        }
+
+        Rule parent = Rule.create()
+            .setRepositoryKey(valuesRule.get("parentRepositoryKey"))
+            .setKey(valuesRule.get("parentKey"));
+        rule.setParent(parent)
+            .setRepositoryKey(valuesRule.get("repositoryKey"))
+            .setKey(valuesRule.get("key"))
+            .setName(valuesRule.get("name"))
+            .setDescription(valuesRule.get("description"))
+            .setSeverity(RulePriority.valueOf(valuesRule.get("level")));
+        return rule;
+      }
+
+      public boolean canConvert(Class type) {
+        return Rule.class.equals(type);
+      }
+    });
+  }
+
+  private void writeNode(HierarchicalStreamWriter writer, String name, String value) {
+    writer.startNode(name);
+    writer.setValue(value);
+    writer.endNode();
+  }
+
+  private Map<String, String> readNode(HierarchicalStreamReader reader) {
+    Map<String, String> values = new HashMap<String, String>();
+    while (reader.hasMoreChildren()) {
+      reader.moveDown();
+      values.put(reader.getNodeName(), reader.getValue());
+      reader.moveUp();
+    }
+    return values;
+  }
+}
index 0426ed45b76840f5876a692797d1133fc8ef1697..123fc534b54c710c82a3d6201ca8f2fb7577d03e 100644 (file)
@@ -24,6 +24,7 @@ import org.apache.commons.lang.builder.ToStringBuilder;
 import org.sonar.api.database.configuration.Property;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rules.Rule;
 
 import java.util.Collection;
 import java.util.Date;
@@ -41,6 +42,8 @@ public class SonarConfig {
 
   private Collection<RulesProfile> profiles;
 
+  private Collection<Rule> rules;
+
   public SonarConfig() {
   }
 
@@ -89,6 +92,14 @@ public class SonarConfig {
     this.profiles = profiles;
   }
 
+  public Collection<Rule> getRules() {
+    return rules;
+  }
+
+  public void setRules(Collection<Rule> rules) {
+    this.rules = rules;
+  }
+
   @Override
   public String toString() {
     return new ToStringBuilder(this)
@@ -99,4 +110,5 @@ public class SonarConfig {
         .append("profiles", profiles)
         .toString();
   }
+
 }
index 4d6e66d7a0f37276df7155c526974fc6915129be..6b28a22366ebb4cba32b4fbde721f76a5f384be7 100644 (file)
@@ -65,7 +65,8 @@ public class BackupTest {
 
   @Test
   public void shouldReturnAValidXml() throws Exception {
-    Backup backup = new Backup(Arrays.asList(new MetricsBackup(null), new PropertiesBackup(null), new ProfilesBackup((DatabaseSession) null)));
+    Backup backup = new Backup(Arrays.asList(new MetricsBackup(null), new PropertiesBackup(null),
+        new ProfilesBackup((DatabaseSession) null)));
     SonarConfig sonarConfig = getSonarConfig();
     sonarConfig.setMetrics(getMetrics());
     sonarConfig.setProperties(getProperties());
@@ -97,7 +98,8 @@ public class BackupTest {
 
   @Test
   public void shouldImportXml() {
-    Backup backup = new Backup(Arrays.asList(new MetricsBackup(null), new PropertiesBackup(null), new ProfilesBackup((DatabaseSession) null)));
+    Backup backup = new Backup(Arrays.asList(new MetricsBackup(null), new PropertiesBackup(null),
+        new ProfilesBackup((DatabaseSession) null)));
 
     String xml = getFileFromClasspath("backup-restore-valid.xml");
     SonarConfig sonarConfig = backup.getSonarConfigFromXml(xml);
@@ -124,7 +126,7 @@ public class BackupTest {
     assertEquals(RulePriority.MAJOR, testActiveRule.getSeverity());
     assertNotNull(testActiveRule.getRule());
     assertEquals("test key", testActiveRule.getRule().getKey());
-    assertEquals("test plugin", testActiveRule.getRule().getPluginName());
+    assertEquals("test plugin", testActiveRule.getRule().getRepositoryKey());
     assertEquals(1, testActiveRule.getActiveRuleParams().size());
 
     ActiveRuleParam testActiveRuleParam = testActiveRule.getActiveRuleParams().get(0);
diff --git a/sonar-server/src/test/java/org/sonar/server/configuration/RulesBackupTest.java b/sonar-server/src/test/java/org/sonar/server/configuration/RulesBackupTest.java
new file mode 100644 (file)
index 0000000..9886fdc
--- /dev/null
@@ -0,0 +1,55 @@
+package org.sonar.server.configuration;
+
+import org.junit.Test;
+import org.sonar.api.rules.Rule;
+import org.sonar.jpa.dao.RulesDao;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public class RulesBackupTest extends AbstractDbUnitTestCase {
+
+  @Test
+  public void shouldExportRules() {
+    SonarConfig sonarConfig = new SonarConfig();
+
+    Rule rule = Rule.create("repo", "key", "name").setDescription("description");
+
+    Rule userRule = Rule.create("repo", "key2", "name2").setDescription("description2");
+    userRule.setParent(rule);
+    userRule.createParameter("param").setDefaultValue("value");
+
+    RulesBackup rulesBackup = new RulesBackup(Arrays.asList(userRule));
+    rulesBackup.exportXml(sonarConfig);
+
+    assertThat(sonarConfig.getRules().size(), is(1));
+    assertTrue(sonarConfig.getRules().iterator().next() == userRule);
+  }
+
+  @Test
+  public void shouldImportRules() {
+    RulesDao rulesDao = getDao().getRulesDao();
+
+    RulesBackup rulesBackup = new RulesBackup(getSession());
+    SonarConfig sonarConfig = new SonarConfig();
+
+    Rule rule = Rule.create("repo", "key", "name").setDescription("description");
+
+    Rule userRule = Rule.create("repo", "key2", "name2").setDescription("description2");
+    userRule.setParent(rule);
+    userRule.createParameter("param").setDefaultValue("value");
+
+    getSession().save(rule);
+
+    sonarConfig.setRules(Arrays.asList(userRule));
+    rulesBackup.importXml(sonarConfig);
+
+    List<Rule> rules = rulesDao.getRules();
+    assertThat(rules.size(), is(2));
+  }
+}