]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-4602 Invalidate dryRun cache when changing quality profiles
authorJulien HENRY <julien.henry@sonarsource.com>
Wed, 4 Sep 2013 08:57:46 +0000 (10:57 +0200)
committerJulien HENRY <julien.henry@sonarsource.com>
Wed, 4 Sep 2013 08:58:30 +0000 (10:58 +0200)
20 files changed:
sonar-core/src/main/java/org/sonar/core/dryrun/DryRunCache.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/dryrun/package-info.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/persistence/DryRunDatabaseFactory.java
sonar-core/src/main/java/org/sonar/core/properties/PropertiesDao.java
sonar-core/src/main/java/org/sonar/core/properties/PropertiesMapper.java
sonar-core/src/main/resources/org/sonar/core/properties/PropertiesMapper.xml
sonar-core/src/test/java/org/sonar/core/persistence/DryRunDatabaseFactoryTest.java
sonar-core/src/test/java/org/sonar/core/properties/PropertiesDaoTest.java
sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteAllProperties-result.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteAllProperties.xml [new file with mode: 0644]
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/ProfilesManager.java
sonar-server/src/main/java/org/sonar/server/configuration/PropertiesBackup.java
sonar-server/src/main/java/org/sonar/server/platform/Platform.java
sonar-server/src/main/java/org/sonar/server/startup/CleanDryRunCache.java
sonar-server/src/test/java/org/sonar/server/configuration/BackupTest.java
sonar-server/src/test/java/org/sonar/server/configuration/InheritedProfilesTest.java
sonar-server/src/test/java/org/sonar/server/configuration/ProfilesManagerTest.java
sonar-server/src/test/java/org/sonar/server/configuration/RuleChangeTest.java

diff --git a/sonar-core/src/main/java/org/sonar/core/dryrun/DryRunCache.java b/sonar-core/src/main/java/org/sonar/core/dryrun/DryRunCache.java
new file mode 100644 (file)
index 0000000..6c8d86d
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.dryrun;
+
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.platform.ServerFileSystem;
+import org.sonar.core.properties.PropertiesDao;
+import org.sonar.core.properties.PropertyDto;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
+
+import javax.annotation.Nullable;
+
+import java.io.File;
+
+/**
+ * @since 4.0
+ */
+public class DryRunCache {
+
+  public static final String SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY = "sonar.dryRun.cache.lastUpdate";
+
+  private ServerFileSystem serverFileSystem;
+  private PropertiesDao propertiesDao;
+  private ResourceDao resourceDao;
+
+  public DryRunCache(ServerFileSystem serverFileSystem, PropertiesDao propertiesDao, ResourceDao resourceDao) {
+    this.serverFileSystem = serverFileSystem;
+    this.propertiesDao = propertiesDao;
+    this.resourceDao = resourceDao;
+  }
+
+  private File getRootCacheLocation() {
+    return new File(serverFileSystem.getTempDir(), "dryRun");
+  }
+
+  public File getCacheLocation(@Nullable Long projectId) {
+    return new File(getRootCacheLocation(), projectId != null ? projectId.toString() : "default");
+  }
+
+  public long getModificationTimestamp(@Nullable Long projectId) {
+    if (projectId == null) {
+      PropertyDto dto = propertiesDao.selectGlobalProperty(SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY);
+      if (dto == null) {
+        return 0;
+      }
+      return Long.valueOf(dto.getValue());
+    }
+    // For modules look for root project last modification timestamp
+    Long rootId = resourceDao.getRootProjectByComponentId(projectId).getId();
+    PropertyDto dto = propertiesDao.selectProjectProperty(rootId, SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY);
+    if (dto == null) {
+      return 0;
+    }
+    return Long.valueOf(dto.getValue());
+  }
+
+  public void cleanAll() {
+    // Delete folder where dryRun DB are stored
+    FileUtils.deleteQuietly(getRootCacheLocation());
+    // Delete all lastUpdate properties to force generation of new DB
+    propertiesDao.deleteAllProperties(SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY);
+  }
+
+  public void clean(@Nullable Long resourceId) {
+    // Delete folder where dryRun DB are stored
+    FileUtils.deleteQuietly(getCacheLocation(resourceId));
+  }
+
+  public void reportGlobalModification() {
+    // Delete folder where dryRun DB are stored
+    FileUtils.deleteQuietly(getRootCacheLocation());
+
+    propertiesDao.setProperty(new PropertyDto().setKey(SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY).setValue(String.valueOf(System.nanoTime())));
+  }
+
+  public void reportResourceModification(long projectId) {
+    // Delete folder where dryRun DB are stored
+    FileUtils.deleteQuietly(getCacheLocation(projectId));
+
+    ResourceDto rootProject = resourceDao.getRootProjectByComponentId(projectId);
+    propertiesDao.setProperty(new PropertyDto().setKey(SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY).setResourceId(rootProject.getId())
+      .setValue(String.valueOf(System.nanoTime())));
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/dryrun/package-info.java b/sonar-core/src/main/java/org/sonar/core/dryrun/package-info.java
new file mode 100644 (file)
index 0000000..fbdd95c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.core.dryrun;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
index 979d956f4824157a6a7b591c8c739763806a923e..795714c3a6aa0843d7ae670e95fe063ba62c66f6 100644 (file)
@@ -27,11 +27,9 @@ import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.ServerComponent;
-import org.sonar.api.config.Settings;
 import org.sonar.api.issue.Issue;
-import org.sonar.api.platform.ServerFileSystem;
 import org.sonar.api.utils.SonarException;
-import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.dryrun.DryRunCache;
 
 import javax.annotation.Nullable;
 import javax.sql.DataSource;
@@ -44,8 +42,6 @@ import java.util.SortedSet;
 import java.util.TreeSet;
 
 public class DryRunDatabaseFactory implements ServerComponent {
-  public static final String SONAR_DRY_RUN_CACHE_KEY_PREFIX = "sonar.dryRun.cache.";
-  public static final String SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY = SONAR_DRY_RUN_CACHE_KEY_PREFIX + "lastUpdate";
   private static final Logger LOG = LoggerFactory.getLogger(DryRunDatabaseFactory.class);
   private static final String DIALECT = "h2";
   private static final String DRIVER = "org.h2.Driver";
@@ -56,31 +52,15 @@ public class DryRunDatabaseFactory implements ServerComponent {
   private static final String PASSWORD = SONAR;
 
   private final Database database;
-  private final ServerFileSystem serverFileSystem;
-  private final Settings settings;
-  private final ResourceDao resourceDao;
+  private DryRunCache dryRunCache;
 
-  public static String getCacheLastUpdateKey(Long rootProjectId) {
-    return SONAR_DRY_RUN_CACHE_KEY_PREFIX + rootProjectId + ".lastUpdate";
-  }
-
-  public DryRunDatabaseFactory(Database database, ServerFileSystem serverFileSystem, Settings settings, ResourceDao resourceDao) {
+  public DryRunDatabaseFactory(Database database, DryRunCache dryRunCache) {
     this.database = database;
-    this.serverFileSystem = serverFileSystem;
-    this.settings = settings;
-    this.resourceDao = resourceDao;
-  }
-
-  private File getRootCacheLocation() {
-    return new File(serverFileSystem.getTempDir(), "dryRun");
-  }
-
-  private File getCacheLocation(@Nullable Long projectId) {
-    return new File(getRootCacheLocation(), projectId != null ? projectId.toString() : "default");
+    this.dryRunCache = dryRunCache;
   }
 
   private Long getLastTimestampInCache(@Nullable Long projectId) {
-    File cacheLocation = getCacheLocation(projectId);
+    File cacheLocation = dryRunCache.getCacheLocation(projectId);
     if (!cacheLocation.exists()) {
       return null;
     }
@@ -105,14 +85,12 @@ public class DryRunDatabaseFactory implements ServerComponent {
   }
 
   private boolean isValid(@Nullable Long projectId, long lastTimestampInCache) {
-    long globalTimestamp = settings.getLong(SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY);
+    long globalTimestamp = dryRunCache.getModificationTimestamp(null);
     if (globalTimestamp > lastTimestampInCache) {
       return false;
     }
     if (projectId != null) {
-      // For modules look for root project last modification timestamp
-      Long rootId = resourceDao.getRootProjectByComponentId(projectId).getId();
-      long projectTimestamp = settings.getLong(getCacheLastUpdateKey(rootId));
+      long projectTimestamp = dryRunCache.getModificationTimestamp(projectId);
       if (projectTimestamp > lastTimestampInCache) {
         return false;
       }
@@ -126,16 +104,12 @@ public class DryRunDatabaseFactory implements ServerComponent {
     Long lastTimestampInCache = getLastTimestampInCache(projectId);
     if (lastTimestampInCache == null || !isValid(projectId, lastTimestampInCache)) {
       lastTimestampInCache = System.nanoTime();
-      cleanCache(projectId);
+      dryRunCache.clean(projectId);
       createNewDatabaseForDryRun(projectId, startup, lastTimestampInCache);
     }
     return dbFileContent(projectId, lastTimestampInCache);
   }
 
-  private void cleanCache(@Nullable Long projectId) {
-    FileUtils.deleteQuietly(getCacheLocation(projectId));
-  }
-
   public String getH2DBName(File location, long timestamp) {
     return location.getAbsolutePath() + File.separator + timestamp;
   }
@@ -145,8 +119,8 @@ public class DryRunDatabaseFactory implements ServerComponent {
   }
 
   private void createNewDatabaseForDryRun(Long projectId, long startup, Long lastTimestampInCache) {
-    String tmpName = getTemporaryH2DBName(getCacheLocation(projectId), lastTimestampInCache);
-    String finalName = getH2DBName(getCacheLocation(projectId), lastTimestampInCache);
+    String tmpName = getTemporaryH2DBName(dryRunCache.getCacheLocation(projectId), lastTimestampInCache);
+    String finalName = getH2DBName(dryRunCache.getCacheLocation(projectId), lastTimestampInCache);
 
     try {
       DataSource source = database.getDataSource();
@@ -234,7 +208,7 @@ public class DryRunDatabaseFactory implements ServerComponent {
   }
 
   private byte[] dbFileContent(@Nullable Long projectId, long timestamp) {
-    File cacheLocation = getCacheLocation(projectId);
+    File cacheLocation = dryRunCache.getCacheLocation(projectId);
     try {
       FileUtils.forceMkdir(cacheLocation);
     } catch (IOException e) {
index dd4dae1138da2e0439af4cb0223b1ec5fd7c1304..ab65fdde04143b6ad3fd7492c77fa62002c06516 100644 (file)
@@ -100,6 +100,16 @@ public class PropertiesDao implements BatchComponent, ServerComponent {
     }
   }
 
+  public PropertyDto selectProjectProperty(long resourceId, String propertyKey) {
+    SqlSession session = mybatis.openSession();
+    PropertiesMapper mapper = session.getMapper(PropertiesMapper.class);
+    try {
+      return mapper.selectByKey(new PropertyDto().setKey(propertyKey).setResourceId(resourceId));
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
   public void setProperty(PropertyDto property) {
     SqlSession session = mybatis.openSession();
     PropertiesMapper mapper = session.getMapper(PropertiesMapper.class);
@@ -143,6 +153,18 @@ public class PropertiesDao implements BatchComponent, ServerComponent {
     }
   }
 
+  public void deleteAllProperties(String key) {
+    SqlSession session = mybatis.openSession();
+    PropertiesMapper mapper = session.getMapper(PropertiesMapper.class);
+    try {
+      mapper.deleteAllProperties(key);
+      session.commit();
+
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
   public void saveGlobalProperties(Map<String, String> properties) {
     SqlSession session = mybatis.openBatchSession();
     PropertiesMapper mapper = session.getMapper(PropertiesMapper.class);
index 409982b2b3bad9550904fbe76c1a7d3788a126a8..d881040cf84549cb58ba2aec4b5e85f60917ac0c 100644 (file)
@@ -45,6 +45,8 @@ public interface PropertiesMapper {
 
   void deleteGlobalProperty(String key);
 
+  void deleteAllProperties(String key);
+
   void deleteGlobalProperties();
 
   void renamePropertyKey(@Param("oldKey") String oldKey, @Param("newKey") String newKey);
index d12932595abea10d132f4ee3e2316b7ab509074c..139e05508f43a92dc8f38ba89dc8bea5f58aeb51 100644 (file)
     delete from properties where resource_id is null and user_id is null
   </delete>
 
+  <delete id="deleteAllProperties" parameterType="string">
+    delete from properties where prop_key=#{id}
+  </delete>
+
   <update id="renamePropertyKey" parameterType="map">
     update properties set prop_key = #{newKey} where prop_key=#{oldKey}
   </update>
index bb5c335a4ca8b5cff02a23e8e047939b5cb3d8e1..0875e0aba59dd8e56408867853aedf331b3d3ce9 100644 (file)
@@ -28,38 +28,36 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
-import org.sonar.api.config.Settings;
-import org.sonar.api.platform.ServerFileSystem;
-import org.sonar.core.resource.ResourceDao;
-import org.sonar.core.resource.ResourceDto;
+import org.sonar.core.dryrun.DryRunCache;
 
 import java.io.File;
 import java.io.IOException;
 import java.sql.SQLException;
 
 import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
   DryRunDatabaseFactory localDatabaseFactory;
-  ServerFileSystem serverFileSystem = mock(ServerFileSystem.class);
   BasicDataSource dataSource;
 
   @Rule
   public TemporaryFolder temporaryFolder = new TemporaryFolder();
-  private File dryRunCache;
-  private ResourceDao resourceDao;
-  private Settings settings;
+  private File dryRunCacheFolder;
+  private DryRunCache dryRunCache;
 
   @Before
   public void setUp() throws Exception {
     File tempFolder = temporaryFolder.newFolder();
-    dryRunCache = new File(tempFolder, "dryRun");
-    when(serverFileSystem.getTempDir()).thenReturn(tempFolder);
-    resourceDao = mock(ResourceDao.class);
-    settings = new Settings();
-    localDatabaseFactory = new DryRunDatabaseFactory(getDatabase(), serverFileSystem, settings, resourceDao);
+    dryRunCacheFolder = new File(tempFolder, "dryRun");
+    dryRunCache = mock(DryRunCache.class);
+    when(dryRunCache.getCacheLocation(anyLong())).thenReturn(dryRunCacheFolder);
+    when(dryRunCache.getModificationTimestamp(null)).thenReturn(0L);
+    when(dryRunCache.getModificationTimestamp(anyLong())).thenReturn(0L);
+    localDatabaseFactory = new DryRunDatabaseFactory(getDatabase(), dryRunCache);
   }
 
   @After
@@ -73,7 +71,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
   public void should_create_database_without_project() throws IOException, SQLException {
     setupData("should_create_database");
 
-    assertThat(new File(dryRunCache, "default")).doesNotExist();
+    assertThat(dryRunCacheFolder).doesNotExist();
 
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(null);
     dataSource = createDatabase(database);
@@ -82,14 +80,14 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
     assertThat(rowCount("projects")).isZero();
     assertThat(rowCount("alerts")).isEqualTo(1);
 
-    assertThat(new File(dryRunCache, "default")).isDirectory();
+    assertThat(dryRunCacheFolder).isDirectory();
   }
 
   @Test
   public void should_reuse_database_without_project() throws IOException, SQLException {
     setupData("should_create_database");
 
-    FileUtils.write(new File(new File(dryRunCache, "default"), "123456.h2.db"), "fakeDbContent");
+    FileUtils.write(new File(dryRunCacheFolder, "123456.h2.db"), "fakeDbContent");
 
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(null);
 
@@ -101,11 +99,11 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
     setupData("should_create_database");
 
     // There is a DB in cache
-    File existingDb = new File(new File(dryRunCache, "default"), "123456.h2.db");
+    File existingDb = new File(dryRunCacheFolder, "123456.h2.db");
     FileUtils.write(existingDb, "fakeDbContent");
 
     // But last modification timestamp is greater
-    settings.setProperty("sonar.dryRun.cache.lastUpdate", "123457");
+    when(dryRunCache.getModificationTimestamp(null)).thenReturn(123457L);
 
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(null);
     dataSource = createDatabase(database);
@@ -115,14 +113,14 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
     assertThat(rowCount("alerts")).isEqualTo(1);
 
     // Previous cached DB was deleted
-    assertThat(existingDb).doesNotExist();
+    verify(dryRunCache).clean(null);
   }
 
   @Test
   public void should_create_database_with_project() throws IOException, SQLException {
     setupData("should_create_database");
 
-    assertThat(new File(dryRunCache, "123")).doesNotExist();
+    assertThat(dryRunCacheFolder).doesNotExist();
 
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(123L);
     dataSource = createDatabase(database);
@@ -132,16 +130,15 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
     assertThat(rowCount("snapshots")).isEqualTo(1);
     assertThat(rowCount("project_measures")).isEqualTo(1);
 
-    assertThat(new File(dryRunCache, "123")).isDirectory();
+    assertThat(dryRunCacheFolder).isDirectory();
   }
 
   @Test
   public void should_reuse_database_with_project() throws IOException, SQLException {
     setupData("should_create_database");
 
-    FileUtils.write(new File(new File(dryRunCache, "123"), "123456.h2.db"), "fakeDbContent");
+    FileUtils.write(new File(dryRunCacheFolder, "123456.h2.db"), "fakeDbContent");
 
-    when(resourceDao.getRootProjectByComponentId(123L)).thenReturn(new ResourceDto().setId(123L));
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(123L);
 
     assertThat(new String(database, Charsets.UTF_8)).isEqualTo("fakeDbContent");
@@ -151,14 +148,12 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
   public void should_evict_database_with_project() throws IOException, SQLException {
     setupData("should_create_database");
 
-    when(resourceDao.getRootProjectByComponentId(123L)).thenReturn(new ResourceDto().setId(123L));
-
     // There is a DB in cache
-    File existingDb = new File(new File(dryRunCache, "123"), "123456.h2.db");
+    File existingDb = new File(dryRunCacheFolder, "123456.h2.db");
     FileUtils.write(existingDb, "fakeDbContent");
 
     // But last project modification timestamp is greater
-    settings.setProperty("sonar.dryRun.cache.123.lastUpdate", "123457");
+    when(dryRunCache.getModificationTimestamp(123L)).thenReturn(123457L);
 
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(123L);
     dataSource = createDatabase(database);
@@ -169,7 +164,7 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
     assertThat(rowCount("project_measures")).isEqualTo(1);
 
     // Previous cached DB was deleted
-    assertThat(existingDb).doesNotExist();
+    verify(dryRunCache).clean(123L);
   }
 
   @Test
@@ -186,8 +181,6 @@ public class DryRunDatabaseFactoryTest extends AbstractDaoTestCase {
   public void should_export_issues_of_project_tree() throws IOException, SQLException {
     setupData("multi-modules-with-issues");
 
-    when(serverFileSystem.getTempDir()).thenReturn(temporaryFolder.newFolder());
-
     // 300 : root module -> export issues of all modules
     byte[] database = localDatabaseFactory.createDatabaseForDryRun(300L);
     dataSource = createDatabase(database);
index d6f98d5e1c5a0660b0e04a3bcfa7282446b9a3ea..1e9de300df16441bce01489dd376d11f35330a14 100644 (file)
@@ -174,6 +174,15 @@ public class PropertiesDaoTest extends AbstractDaoTestCase {
     checkTables("deleteGlobalProperty", "properties");
   }
 
+  @Test
+  public void deleteAllProperties() {
+    setupData("deleteAllProperties");
+
+    dao.deleteAllProperties("to_be_deleted");
+
+    checkTables("deleteAllProperties", "properties");
+  }
+
   @Test
   public void insertGlobalProperties() {
     setupData("insertGlobalProperties");
diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteAllProperties-result.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteAllProperties-result.xml
new file mode 100644 (file)
index 0000000..ba4619f
--- /dev/null
@@ -0,0 +1,15 @@
+<dataset>
+
+  <!-- global -->
+  <!-- <properties id="1" prop_key="to_be_deleted" text_value="new_global" resource_id="[null]" user_id="[null]"/> -->
+  <properties id="2" prop_key="global.key" text_value="new_global" resource_id="[null]" user_id="[null]"/>
+
+  <!-- project -->
+  <!--  <properties id="3" prop_key="to_be_deleted" text_value="new_project" resource_id="10" user_id="[null]"/> -->
+  <properties id="4" prop_key="project.key" text_value="new_project" resource_id="10" user_id="[null]"/>
+
+  <!-- user -->
+  <!-- <properties id="5" prop_key="to_be_deleted" text_value="new_user" resource_id="[null]" user_id="100"/> -->
+  <properties id="6" prop_key="user.key" text_value="new_user" resource_id="[null]" user_id="100"/>
+
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteAllProperties.xml b/sonar-core/src/test/resources/org/sonar/core/properties/PropertiesDaoTest/deleteAllProperties.xml
new file mode 100644 (file)
index 0000000..0953b0e
--- /dev/null
@@ -0,0 +1,15 @@
+<dataset>
+
+  <!-- global -->
+  <properties id="1" prop_key="to_be_deleted" text_value="new_global" resource_id="[null]" user_id="[null]"/>
+  <properties id="2" prop_key="global.key" text_value="new_global" resource_id="[null]" user_id="[null]"/>
+
+  <!-- project -->
+  <properties id="3" prop_key="to_be_deleted" text_value="new_project" resource_id="10" user_id="[null]"/>
+  <properties id="4" prop_key="project.key" text_value="new_project" resource_id="10" user_id="[null]"/>
+
+  <!-- user -->
+  <properties id="5" prop_key="to_be_deleted" text_value="new_user" resource_id="[null]" user_id="100"/>
+  <properties id="6" prop_key="user.key" text_value="new_user" resource_id="[null]" user_id="100"/>
+
+</dataset>
index 8f52f8d9955942e3a8f7f4f922aa4e4a2011f40b..1af960ed22651d34defc0ebd350b9e7ef08ab257 100644 (file)
@@ -30,9 +30,9 @@ import org.apache.commons.lang.CharEncoding;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.database.DatabaseSession;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.core.persistence.DatabaseVersion;
 import org.sonar.server.platform.PersistentSettings;
-import org.sonar.server.startup.CleanDryRunCache;
 
 import javax.annotation.Nullable;
 
@@ -55,7 +55,7 @@ public class Backup {
     backupables = new ArrayList<Backupable>();
   }
 
-  public Backup(DatabaseSession session, PersistentSettings persistentSettings, CleanDryRunCache cleanDryRunCache) {
+  public Backup(DatabaseSession session, PersistentSettings persistentSettings, DryRunCache dryRunCache) {
     this();
     this.session = session;
 
@@ -63,7 +63,7 @@ public class Backup {
     backupables.add(new PropertiesBackup(persistentSettings));
     // Note that order is important, because profile can have reference to rule
     backupables.add(new RulesBackup(session));
-    backupables.add(new ProfilesBackup(session));
+    backupables.add(new ProfilesBackup(session, dryRunCache));
   }
 
   /**
index fa9e337d96600327a2c214f8a86ef93d871766d3..397182c71340947229ec359847d066065bea52c2 100644 (file)
@@ -31,10 +31,20 @@ import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.measures.Metric;
 import org.sonar.api.profiles.Alert;
 import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.rules.*;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.ActiveRuleParam;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleParam;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.jpa.dao.RulesDao;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
 public class ProfilesBackup implements Backupable {
 
@@ -52,9 +62,11 @@ public class ProfilesBackup implements Backupable {
 
   private Collection<RulesProfile> profiles;
   private DatabaseSession session;
+  private DryRunCache dryRunCache;
 
-  public ProfilesBackup(DatabaseSession session) {
+  public ProfilesBackup(DatabaseSession session, DryRunCache dryRunCache) {
     this.session = session;
+    this.dryRunCache = dryRunCache;
   }
 
   /**
@@ -95,7 +107,7 @@ public class ProfilesBackup implements Backupable {
   public void importXml(SonarConfig sonarConfig) {
     if (sonarConfig.getProfiles() != null && !sonarConfig.getProfiles().isEmpty()) {
       LoggerFactory.getLogger(getClass()).info("Delete profiles");
-      ProfilesManager profilesManager = new ProfilesManager(session, null);
+      ProfilesManager profilesManager = new ProfilesManager(session, null, dryRunCache);
       profilesManager.deleteAllProfiles();
 
       RulesDao rulesDao = new RulesDao(session);
@@ -122,7 +134,7 @@ public class ProfilesBackup implements Backupable {
 
   private void importAlerts(RulesProfile profile) {
     if (profile.getAlerts() != null) {
-      for (Iterator<Alert> ia = profile.getAlerts().iterator(); ia.hasNext(); ) {
+      for (Iterator<Alert> ia = profile.getAlerts().iterator(); ia.hasNext();) {
         Alert alert = ia.next();
         Metric unMarshalledMetric = alert.getMetric();
         String validKey = unMarshalledMetric.getKey();
@@ -139,7 +151,7 @@ public class ProfilesBackup implements Backupable {
   }
 
   private void importActiveRules(RulesDao rulesDao, RulesProfile profile) {
-    for (Iterator<ActiveRule> iar = profile.getActiveRules(true).iterator(); iar.hasNext(); ) {
+    for (Iterator<ActiveRule> iar = profile.getActiveRules(true).iterator(); iar.hasNext();) {
       ActiveRule activeRule = iar.next();
       Rule unMarshalledRule = activeRule.getRule();
       Rule matchingRuleInDb = rulesDao.getRuleByKey(unMarshalledRule.getRepositoryKey(), unMarshalledRule.getKey());
@@ -151,7 +163,7 @@ public class ProfilesBackup implements Backupable {
         activeRule.setRule(matchingRuleInDb);
         activeRule.setRulesProfile(profile);
         activeRule.getActiveRuleParams();
-        for (Iterator<ActiveRuleParam> irp = activeRule.getActiveRuleParams().iterator(); irp.hasNext(); ) {
+        for (Iterator<ActiveRuleParam> irp = activeRule.getActiveRuleParams().iterator(); irp.hasNext();) {
           ActiveRuleParam activeRuleParam = irp.next();
           RuleParam unMarshalledRP = activeRuleParam.getRuleParam();
           RuleParam matchingRPInDb = rulesDao.getRuleParam(matchingRuleInDb, unMarshalledRP.getKey());
index 2a040e860e3bd95c5518d1f2c531750b4b696cb1..11dde4538e21218be0eaef00d0f1d762a2a1306a 100644 (file)
@@ -24,30 +24,37 @@ import org.apache.commons.lang.ObjectUtils;
 import org.apache.commons.lang.StringUtils;
 import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.profiles.RulesProfile;
-import org.sonar.api.rules.*;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.ActiveRuleChange;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleParam;
+import org.sonar.api.rules.RulePriority;
 import org.sonar.api.utils.ValidationMessages;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.jpa.dao.BaseDao;
 import org.sonar.jpa.dao.RulesDao;
 
 import java.util.List;
 
-
 public class ProfilesManager extends BaseDao {
 
   private RulesDao rulesDao;
+  private DryRunCache dryRunCache;
 
-  public ProfilesManager(DatabaseSession session, RulesDao rulesDao) {
+  public ProfilesManager(DatabaseSession session, RulesDao rulesDao, DryRunCache dryRunCache) {
     super(session);
     this.rulesDao = rulesDao;
+    this.dryRunCache = dryRunCache;
   }
 
   public void copyProfile(int profileId, String newProfileName) {
     RulesProfile profile = getSession().getSingleResult(RulesProfile.class, "id", profileId);
     RulesProfile toImport = (RulesProfile) profile.clone();
     toImport.setName(newProfileName);
-    ProfilesBackup pb = new ProfilesBackup(getSession());
+    ProfilesBackup pb = new ProfilesBackup(getSession(), dryRunCache);
     pb.importProfile(rulesDao, toImport);
     getSession().commit();
+    dryRunCache.reportGlobalModification();
   }
 
   public void deleteAllProfiles() {
@@ -60,6 +67,7 @@ public class ProfilesManager extends BaseDao {
       getSession().removeWithoutFlush(profile);
     }
     getSession().commit();
+    dryRunCache.reportGlobalModification();
   }
 
   // Managing inheritance of profiles
@@ -89,6 +97,7 @@ public class ProfilesManager extends BaseDao {
       profile.setParentName(newParent == null ? null : newParent.getName());
       getSession().saveWithoutFlush(profile);
       getSession().commit();
+      dryRunCache.reportGlobalModification();
     }
     return messages;
   }
@@ -116,6 +125,7 @@ public class ProfilesManager extends BaseDao {
       removeActiveRule(activeRuleToRemove);
     }
     getSession().commit();
+    dryRunCache.reportGlobalModification();
   }
 
   /**
@@ -168,6 +178,7 @@ public class ProfilesManager extends BaseDao {
       activateOrChange(child, parentActiveRule, userName);
     }
     getSession().commit();
+    dryRunCache.reportGlobalModification();
   }
 
   /**
@@ -181,6 +192,7 @@ public class ProfilesManager extends BaseDao {
       deactivate(child, parentActiveRule.getRule(), userName);
     }
     getSession().commit();
+    dryRunCache.reportGlobalModification();
   }
 
   /**
@@ -216,6 +228,7 @@ public class ProfilesManager extends BaseDao {
       }
 
       getSession().commit();
+      dryRunCache.reportGlobalModification();
     }
   }
 
index 8dac49cfe9bc6307130fcc65ee4a5691b81b147c..196af403ecb5bb0cd9a1cc34efbe6c23570c93c2 100644 (file)
@@ -25,7 +25,7 @@ import com.thoughtworks.xstream.XStream;
 import org.slf4j.LoggerFactory;
 import org.sonar.api.CoreProperties;
 import org.sonar.api.database.configuration.Property;
-import org.sonar.core.persistence.DryRunDatabaseFactory;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.core.properties.PropertyDto;
 import org.sonar.server.platform.PersistentSettings;
 
@@ -83,7 +83,7 @@ public class PropertiesBackup implements Backupable {
     // default permissions properties should not be exported as they reference permission_templates entries in the DB
     return !CoreProperties.SERVER_ID.equals(propertyKey)
       && !propertyKey.startsWith(PERMISSION_PROPERTIES_PREFIX)
-      && !propertyKey.startsWith(DryRunDatabaseFactory.SONAR_DRY_RUN_CACHE_KEY_PREFIX);
+      && !DryRunCache.SONAR_DRY_RUN_CACHE_LAST_UPDATE_KEY.equals(propertyKey);
   }
 
   private boolean shouldNotBeErased(String propertyKey) {
index 65acf75027fee0656298ccc3cd856ee177acf785..55f07767e23b92b28493d3cd4576af2046b55918 100644 (file)
@@ -37,6 +37,7 @@ import org.sonar.api.utils.TimeProfiler;
 import org.sonar.api.utils.UriReader;
 import org.sonar.core.component.SnapshotPerspectives;
 import org.sonar.core.config.Logback;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.core.i18n.GwtI18n;
 import org.sonar.core.i18n.I18nManager;
 import org.sonar.core.i18n.RuleI18nManager;
@@ -258,7 +259,6 @@ public final class Platform {
     coreContainer.addSingleton(ThreadLocalDatabaseSessionFactory.class);
     coreContainer.addPicoAdapter(new DatabaseSessionProvider());
     coreContainer.addSingleton(ServerMetadataPersister.class);
-    coreContainer.addSingleton(CleanDryRunCache.class);
     coreContainer.startComponents();
   }
 
@@ -305,6 +305,7 @@ public final class Platform {
     servicesContainer.addSingleton(MeasureFilterExecutor.class);
     servicesContainer.addSingleton(MeasureFilterEngine.class);
     servicesContainer.addSingleton(DryRunDatabaseFactory.class);
+    servicesContainer.addSingleton(DryRunCache.class);
     servicesContainer.addSingleton(DefaultResourcePermissions.class);
     servicesContainer.addSingleton(Periods.class);
 
@@ -389,6 +390,7 @@ public final class Platform {
     startupContainer.addSingleton(RenameDeprecatedPropertyKeys.class);
     startupContainer.addSingleton(LogServerId.class);
     startupContainer.addSingleton(RegisterServletFilters.class);
+    startupContainer.addSingleton(CleanDryRunCache.class);
     startupContainer.startComponents();
 
     startupContainer.getComponentByType(ServerLifecycleNotifier.class).notifyStart();
index 42937faf196fd75574522318e525105d8d688063..d08ccb32e586b19b2a6085870133cff30490ca54 100644 (file)
  */
 package org.sonar.server.startup;
 
-import org.apache.commons.io.FileUtils;
-import org.sonar.core.persistence.DryRunDatabaseFactory;
-import org.sonar.server.platform.DefaultServerFileSystem;
-import org.sonar.server.platform.PersistentSettings;
-
-import java.io.File;
-import java.util.Map;
+import org.sonar.core.dryrun.DryRunCache;
 
 /**
  * @since 4.0
  */
 public class CleanDryRunCache {
 
-  private DefaultServerFileSystem serverFileSystem;
-  private PersistentSettings settings;
-
-  public CleanDryRunCache(DefaultServerFileSystem serverFileSystem, PersistentSettings settings) {
-    this.serverFileSystem = serverFileSystem;
-    this.settings = settings;
-  }
+  private DryRunCache dryRunCache;
 
-  private File getRootCacheLocation() {
-    return new File(serverFileSystem.getTempDir(), "dryRun");
+  public CleanDryRunCache(DryRunCache dryRunCache) {
+    this.dryRunCache = dryRunCache;
   }
 
   public void start() {
-    clean();
-  }
-
-  public void clean() {
-    // Delete folder where dryRun DB are stored
-    FileUtils.deleteQuietly(getRootCacheLocation());
-    // Delete all lastUpdate properties to force generation of new DB
-    Map<String, String> properties = settings.getProperties();
-    for (String propKey : properties.keySet()) {
-      if (propKey.startsWith(DryRunDatabaseFactory.SONAR_DRY_RUN_CACHE_KEY_PREFIX)) {
-        settings.deleteProperty(propKey);
-      }
-    }
+    dryRunCache.cleanAll();
   }
 }
index 8380ab1e966d0812ad5cc1b5e144135e9adb39f3..9fadc4494dd96fa054ecdbc2eb81697665115815 100644 (file)
@@ -24,6 +24,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.CharEncoding;
 import org.custommonkey.xmlunit.Diff;
 import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.database.DatabaseSession;
 import org.sonar.api.database.configuration.Property;
@@ -35,6 +36,7 @@ import org.sonar.api.rules.ActiveRuleParam;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RuleParam;
 import org.sonar.api.rules.RulePriority;
+import org.sonar.core.dryrun.DryRunCache;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -54,6 +56,13 @@ import static org.mockito.Mockito.verify;
 
 public class BackupTest {
 
+  private DryRunCache dryRunCache;
+
+  @Before
+  public void prepare() {
+    this.dryRunCache = mock(DryRunCache.class);
+  }
+
   @Test
   public void shouldExportXml() throws Exception {
     SonarConfig sonarConfig = getSonarConfig();
@@ -68,7 +77,7 @@ public class BackupTest {
   @Test
   public void shouldReturnAValidXml() throws Exception {
     Backup backup = new Backup(Arrays.asList(new MetricsBackup(null), new PropertiesBackup(null),
-      new RulesBackup((DatabaseSession) null), new ProfilesBackup((DatabaseSession) null)));
+      new RulesBackup((DatabaseSession) null), new ProfilesBackup((DatabaseSession) null, dryRunCache)));
     SonarConfig sonarConfig = getSonarConfig();
     sonarConfig.setMetrics(getMetrics());
     sonarConfig.setProperties(getProperties());
@@ -102,7 +111,7 @@ public class BackupTest {
   @Test
   public void shouldImportXml() {
     Backup backup = new Backup(Arrays.asList(new MetricsBackup(null), new PropertiesBackup(null),
-      new RulesBackup((DatabaseSession) null), new ProfilesBackup((DatabaseSession) null)));
+      new RulesBackup((DatabaseSession) null), new ProfilesBackup((DatabaseSession) null, dryRunCache)));
 
     String xml = getFileFromClasspath("backup-restore-valid.xml");
     SonarConfig sonarConfig = backup.getSonarConfigFromXml(xml);
@@ -185,7 +194,7 @@ public class BackupTest {
   @Test
   public void shouldImportXmlWithoutInheritanceInformation() {
     Backup backup = new Backup(Arrays.asList(new MetricsBackup(null), new PropertiesBackup(null),
-      new RulesBackup((DatabaseSession) null), new ProfilesBackup((DatabaseSession) null)));
+      new RulesBackup((DatabaseSession) null), new ProfilesBackup((DatabaseSession) null, dryRunCache)));
 
     String xml = getFileFromClasspath("backup-restore-without-inheritance.xml");
     SonarConfig sonarConfig = backup.getSonarConfigFromXml(xml);
index 34adde489e5addbdd33031afdf9f7768788630ab..63ceb7343c897b622d9541f333511a5e4f6ce471 100644 (file)
@@ -22,18 +22,20 @@ package org.sonar.server.configuration;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.profiles.RulesProfile;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.jpa.test.AbstractDbUnitTestCase;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
 
 public class InheritedProfilesTest extends AbstractDbUnitTestCase {
   private ProfilesManager profilesManager;
 
   @Before
   public void setUp() {
-    profilesManager = new ProfilesManager(getSession(), null);
+    profilesManager = new ProfilesManager(getSession(), null, mock(DryRunCache.class));
   }
 
   @Test
index f3d3f9ee605bb4fd77a0bf86860ca106336a07e8..c1231bda910e7861a403c487fb2b3403fd8b92b9 100644 (file)
@@ -22,10 +22,11 @@ package org.sonar.server.configuration;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.profiles.RulesProfile;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.jpa.test.AbstractDbUnitTestCase;
 
 import static org.fest.assertions.Assertions.assertThat;
-
+import static org.mockito.Mockito.mock;
 
 public class ProfilesManagerTest extends AbstractDbUnitTestCase {
 
@@ -33,7 +34,7 @@ public class ProfilesManagerTest extends AbstractDbUnitTestCase {
 
   @Before
   public void before() {
-    manager = new ProfilesManager(getSession(), null);
+    manager = new ProfilesManager(getSession(), null, mock(DryRunCache.class));
   }
 
   @Test
index 652a0552c535b4f1a279313c31441d7d3cc3059b..dd10096e767769c908c89825a2f486c12f7639eb 100644 (file)
@@ -25,16 +25,18 @@ import org.sonar.api.rules.ActiveRuleChange;
 import org.sonar.api.rules.ActiveRuleParamChange;
 import org.sonar.api.rules.Rule;
 import org.sonar.api.rules.RulePriority;
+import org.sonar.core.dryrun.DryRunCache;
 import org.sonar.jpa.test.AbstractDbUnitTestCase;
 
 import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 
 public class RuleChangeTest extends AbstractDbUnitTestCase {
   private ProfilesManager profilesManager;
 
   @Before
   public void setUp() {
-    profilesManager = new ProfilesManager(getSession(), null);
+    profilesManager = new ProfilesManager(getSession(), null, mock(DryRunCache.class));
   }
 
   @Test
@@ -55,21 +57,21 @@ public class RuleChangeTest extends AbstractDbUnitTestCase {
   public void should_track_rule_activation() {
     setupData("initialData");
     profilesManager.activated(2, 3, "admin");
-    checkTables("ruleActivated", new String[]{"change_date"}, "active_rule_changes");
+    checkTables("ruleActivated", new String[] {"change_date"}, "active_rule_changes");
   }
 
   @Test
   public void should_track_rule_deactivation() {
     setupData("initialData");
     profilesManager.deactivated(2, 3, "admin");
-    checkTables("ruleDeactivated", new String[]{"change_date"}, "active_rule_changes");
+    checkTables("ruleDeactivated", new String[] {"change_date"}, "active_rule_changes");
   }
 
   @Test
   public void should_track_rule_param_change() {
     setupData("initialData");
     profilesManager.ruleParamChanged(2, 3, "param1", "20", "30", "admin");
-    checkTables("ruleParamChanged", new String[]{"change_date"}, "active_rule_changes", "active_rule_param_changes");
+    checkTables("ruleParamChanged", new String[] {"change_date"}, "active_rule_changes", "active_rule_param_changes");
   }
 
   @Test
@@ -83,7 +85,7 @@ public class RuleChangeTest extends AbstractDbUnitTestCase {
   public void should_track_rule_severity_change() {
     setupData("initialData");
     profilesManager.ruleSeverityChanged(2, 3, RulePriority.BLOCKER, RulePriority.CRITICAL, "admin");
-    checkTables("ruleSeverityChanged", new String[]{"change_date"}, "active_rule_changes");
+    checkTables("ruleSeverityChanged", new String[] {"change_date"}, "active_rule_changes");
   }
 
   @Test
@@ -97,7 +99,7 @@ public class RuleChangeTest extends AbstractDbUnitTestCase {
   public void should_track_rule_revert() {
     setupData("ruleReverted");
     profilesManager.revert(2, 3, "admin");
-    checkTables("ruleReverted", new String[]{"change_date"}, "active_rule_changes", "active_rule_param_changes");
+    checkTables("ruleReverted", new String[] {"change_date"}, "active_rule_changes", "active_rule_param_changes");
   }
 
   @Test
@@ -118,7 +120,7 @@ public class RuleChangeTest extends AbstractDbUnitTestCase {
   public void should_track_change_parent_profile() {
     setupData("changeParentProfile");
     profilesManager.changeParentProfile(2, "parent", "admin");
-    checkTables("changeParentProfile", new String[]{"change_date"}, "active_rule_changes");
+    checkTables("changeParentProfile", new String[] {"change_date"}, "active_rule_changes");
   }
 
   @Test
@@ -126,8 +128,7 @@ public class RuleChangeTest extends AbstractDbUnitTestCase {
     setupData("initialData");
     Rule rule = getSession().reattach(Rule.class, 1);
     profilesManager.removeActivatedRules(rule);
-    checkTables("removeActivatedRules", new String[]{"change_date"}, "active_rule_changes", "active_rules");
+    checkTables("removeActivatedRules", new String[] {"change_date"}, "active_rule_changes", "active_rules");
   }
 
-
 }