aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core/src
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2014-10-23 17:15:00 +0200
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2014-10-27 09:08:39 +0100
commit4c64f4f00be61229d4c4c8be8f8bec9b25df2bac (patch)
treeb5844e988c063e80cb3cc6cede99d4f68a61fb7f /sonar-core/src
parent9c467f71a35ebb691acbb77bb1de3d825c4b109d (diff)
downloadsonarqube-4c64f4f00be61229d4c4c8be8f8bec9b25df2bac.tar.gz
sonarqube-4c64f4f00be61229d4c4c8be8f8bec9b25df2bac.zip
SONAR-5628 - Compute Engine - Move DbCleaner
Diffstat (limited to 'sonar-core/src')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/db/AnalysisReportDto.java25
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerConstants.java34
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerProperties.java105
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java96
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java75
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilter.java53
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filter.java31
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filters.java60
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Interval.java74
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilter.java89
-rw-r--r--sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/package-info.java24
-rw-r--r--sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java4
-rw-r--r--sonar-core/src/main/resources/org/sonar/core/computation/db/AnalysisReportMapper.xml5
-rw-r--r--sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerPropertiesTest.java33
-rw-r--r--sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerTestUtils.java45
-rw-r--r--sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTaskTest.java106
-rw-r--r--sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleanerTest.java90
-rw-r--r--sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilterTest.java47
-rw-r--r--sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/IntervalTest.java108
-rw-r--r--sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilterTest.java95
20 files changed, 1188 insertions, 11 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/db/AnalysisReportDto.java b/sonar-core/src/main/java/org/sonar/core/computation/db/AnalysisReportDto.java
index 82c4af9526d..1491a1ae19c 100644
--- a/sonar-core/src/main/java/org/sonar/core/computation/db/AnalysisReportDto.java
+++ b/sonar-core/src/main/java/org/sonar/core/computation/db/AnalysisReportDto.java
@@ -21,12 +21,16 @@ package org.sonar.core.computation.db;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
+import com.google.common.base.Strings;
+import org.sonar.core.component.AuthorizedComponentDto;
+import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.Dto;
import javax.annotation.Nullable;
import java.util.Date;
+import static com.google.common.base.Preconditions.checkNotNull;
import static org.sonar.core.computation.db.AnalysisReportDto.Status.FAILED;
import static org.sonar.core.computation.db.AnalysisReportDto.Status.SUCCESS;
@@ -34,12 +38,12 @@ public class AnalysisReportDto extends Dto<String> {
private Long id;
private String projectKey;
- private String projectName;
private Status status;
private String data;
private Long snapshotId;
private Date startedAt;
private Date finishedAt;
+ private ComponentDto project;
public AnalysisReportDto() {
super();
@@ -102,7 +106,6 @@ public class AnalysisReportDto extends Dto<String> {
return Objects.toStringHelper(this)
.add("id", getId())
.add("projectKey", getProjectKey())
- .add("projectName", getProjectName())
.add("status", getStatus())
.add("createdAt", getCreatedAt())
.add("startedAt", getStartedAt())
@@ -111,12 +114,11 @@ public class AnalysisReportDto extends Dto<String> {
}
public String getProjectName() {
- return projectName;
- }
+ if (project == null) {
+ return getProjectKey();
+ }
- public AnalysisReportDto setProjectName(String projectName) {
- this.projectName = projectName;
- return this;
+ return Strings.nullToEmpty(project.name());
}
public Long getSnapshotId() {
@@ -158,6 +160,15 @@ public class AnalysisReportDto extends Dto<String> {
return this;
}
+ public AuthorizedComponentDto getProject() {
+ return checkNotNull(project);
+ }
+
+ public AnalysisReportDto setProject(ComponentDto project) {
+ this.project = project;
+ return this;
+ }
+
public enum Status {
PENDING, WORKING, SUCCESS, FAILED;
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerConstants.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerConstants.java
new file mode 100644
index 00000000000..a577f632608
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerConstants.java
@@ -0,0 +1,34 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner;
+
+public interface DbCleanerConstants {
+
+ String PLUGIN_KEY = "dbcleaner";
+ String PLUGIN_NAME = "DbCleaner";
+ String PROPERTY_CLEAN_DIRECTORY = "sonar.dbcleaner.cleanDirectory";
+
+ String HOURS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_DAY = "sonar.dbcleaner.hoursBeforeKeepingOnlyOneSnapshotByDay";
+ String WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK = "sonar.dbcleaner.weeksBeforeKeepingOnlyOneSnapshotByWeek";
+ String WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH = "sonar.dbcleaner.weeksBeforeKeepingOnlyOneSnapshotByMonth";
+ String WEEKS_BEFORE_DELETING_ALL_SNAPSHOTS = "sonar.dbcleaner.weeksBeforeDeletingAllSnapshots";
+ String DAYS_BEFORE_DELETING_CLOSED_ISSUES = "sonar.dbcleaner.daysBeforeDeletingClosedIssues";
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerProperties.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerProperties.java
new file mode 100644
index 00000000000..744a8873657
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerProperties.java
@@ -0,0 +1,105 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner;
+
+import org.sonar.api.CoreProperties;
+import org.sonar.api.PropertyType;
+import org.sonar.api.config.PropertyDefinition;
+import org.sonar.api.resources.Qualifiers;
+
+import java.util.Arrays;
+import java.util.List;
+
+public final class DbCleanerProperties {
+
+ public static List<PropertyDefinition> all() {
+ return Arrays.asList(
+ PropertyDefinition.builder(DbCleanerConstants.PROPERTY_CLEAN_DIRECTORY)
+ .defaultValue("true")
+ .name("Clean directory/package history")
+ .description("If set to true, no history is kept at directory/package level. Setting this to false can cause database bloat.")
+ .type(PropertyType.BOOLEAN)
+ .onQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DATABASE_CLEANER)
+ .index(1)
+ .build(),
+
+ PropertyDefinition.builder(DbCleanerConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES)
+ .defaultValue("30")
+ .name("Delete closed issues after")
+ .description("Issues that have been closed for more than this number of days will be deleted.")
+ .type(PropertyType.INTEGER)
+ .onQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DATABASE_CLEANER)
+ .index(2)
+ .build(),
+
+ PropertyDefinition.builder(DbCleanerConstants.HOURS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_DAY)
+ .defaultValue("24")
+ .name("Keep only one snapshot a day after")
+ .description("After this number of hours, if there are several snapshots during the same day, "
+ + "the DbCleaner keeps the most recent one and fully deletes the other ones.")
+ .type(PropertyType.INTEGER)
+ .onQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DATABASE_CLEANER)
+ .index(3)
+ .build(),
+
+ PropertyDefinition.builder(DbCleanerConstants.WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK)
+ .defaultValue("4")
+ .name("Keep only one snapshot a week after")
+ .description("After this number of weeks, if there are several snapshots during the same week, "
+ + "the DbCleaner keeps the most recent one and fully deletes the other ones")
+ .type(PropertyType.INTEGER)
+ .onQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DATABASE_CLEANER)
+ .index(4)
+ .build(),
+
+ PropertyDefinition.builder(DbCleanerConstants.WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH)
+ .defaultValue("52")
+ .name("Keep only one snapshot a month after")
+ .description("After this number of weeks, if there are several snapshots during the same month, "
+ + "the DbCleaner keeps the most recent one and fully deletes the other ones.")
+ .type(PropertyType.INTEGER)
+ .onQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DATABASE_CLEANER)
+ .index(5)
+ .build(),
+
+ PropertyDefinition.builder(DbCleanerConstants.WEEKS_BEFORE_DELETING_ALL_SNAPSHOTS)
+ .defaultValue("260")
+ .name("Delete all snapshots after")
+ .description("After this number of weeks, all snapshots are fully deleted.")
+ .type(PropertyType.INTEGER)
+ .onQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_GENERAL)
+ .subCategory(CoreProperties.SUBCATEGORY_DATABASE_CLEANER)
+ .index(6)
+ .build()
+ );
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java
new file mode 100644
index 00000000000..4cd7799c297
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java
@@ -0,0 +1,96 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.TimeUtils;
+import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner;
+import org.sonar.core.purge.PurgeConfiguration;
+import org.sonar.core.purge.PurgeDao;
+import org.sonar.core.purge.PurgeProfiler;
+
+/**
+ * @since 2.14
+ */
+public class DefaultPurgeTask {
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultPurgeTask.class);
+
+ private PurgeDao purgeDao;
+ private Settings settings;
+ private DefaultPeriodCleaner periodCleaner;
+ private final PurgeProfiler profiler;
+
+ public DefaultPurgeTask(PurgeDao purgeDao, Settings settings, DefaultPeriodCleaner periodCleaner, PurgeProfiler profiler) {
+ this.purgeDao = purgeDao;
+ this.settings = settings;
+ this.periodCleaner = periodCleaner;
+ this.profiler = profiler;
+ }
+
+ public DefaultPurgeTask delete(long resourceId) {
+ purgeDao.deleteResourceTree(resourceId);
+ return this;
+ }
+
+ public DefaultPurgeTask purge(long resourceId) {
+ long start = System.currentTimeMillis();
+ profiler.reset();
+ cleanHistoricalData(resourceId);
+ doPurge(resourceId);
+ if (settings.getBoolean(CoreProperties.PROFILING_LOG_PROPERTY)) {
+ long duration = System.currentTimeMillis() - start;
+ LOG.info("\n -------- Profiling for purge: " + TimeUtils.formatDuration(duration) + " --------\n");
+ profiler.dump(duration, LOG);
+ LOG.info("\n -------- End of profiling for purge --------\n");
+ }
+ return this;
+ }
+
+ private void cleanHistoricalData(long resourceId) {
+ try {
+ periodCleaner.clean(resourceId);
+ } catch (Exception e) {
+ // purge errors must no fail the batch
+ LOG.error("Fail to clean historical data [id=" + resourceId + "]", e);
+ }
+ }
+
+ private void doPurge(long resourceId) {
+ try {
+ purgeDao.purge(newConf(resourceId));
+ } catch (Exception e) {
+ // purge errors must no fail the batch
+ LOG.error("Fail to purge data [id=" + resourceId + "]", e);
+ }
+ }
+
+ private PurgeConfiguration newConf(long resourceId) {
+ String[] scopes = new String[] {Scopes.FILE};
+ if (settings.getBoolean(DbCleanerConstants.PROPERTY_CLEAN_DIRECTORY)) {
+ scopes = new String[] {Scopes.DIRECTORY, Scopes.FILE};
+ }
+ return new PurgeConfiguration(resourceId, scopes, settings.getInt(DbCleanerConstants.DAYS_BEFORE_DELETING_CLOSED_ISSUES));
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java
new file mode 100644
index 00000000000..76b1238dc07
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Project;
+import org.sonar.api.task.TaskExtension;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.purge.PurgeDao;
+import org.sonar.core.purge.PurgeSnapshotQuery;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.List;
+
+public class DefaultPeriodCleaner implements TaskExtension {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultPeriodCleaner.class);
+ private PurgeDao purgeDao;
+ private Settings settings;
+
+ public DefaultPeriodCleaner(PurgeDao purgeDao, Settings settings) {
+ this.purgeDao = purgeDao;
+ this.settings = settings;
+ }
+
+ public void purge(Project project, int projectSnapshotId) {
+ clean(project.getId());
+ }
+
+ public void clean(long projectId) {
+ doClean(projectId, new Filters(settings).all());
+ }
+
+ @VisibleForTesting
+ void doClean(long projectId, List<Filter> filters) {
+ List<PurgeableSnapshotDto> history = selectProjectSnapshots(projectId);
+ for (Filter filter : filters) {
+ filter.log();
+ delete(filter.filter(history));
+ }
+ }
+
+ private void delete(List<PurgeableSnapshotDto> snapshots) {
+ for (PurgeableSnapshotDto snapshot : snapshots) {
+ LOG.info("<- Delete snapshot: " + DateUtils.formatDateTime(snapshot.getDate()) + " [" + snapshot.getSnapshotId() + "]");
+ purgeDao.deleteSnapshots(PurgeSnapshotQuery.create().setRootSnapshotId(snapshot.getSnapshotId()));
+ purgeDao.deleteSnapshots(PurgeSnapshotQuery.create().setId(snapshot.getSnapshotId()));
+ }
+ }
+
+ private List<PurgeableSnapshotDto> selectProjectSnapshots(long resourceId) {
+ return purgeDao.selectPurgeableSnapshots(resourceId);
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilter.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilter.java
new file mode 100644
index 00000000000..25dcb409ea1
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilter.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import com.google.common.collect.Lists;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.Date;
+import java.util.List;
+
+class DeleteAllFilter implements Filter {
+ private final Date before;
+
+ public DeleteAllFilter(Date before) {
+ this.before = before;
+ }
+
+ @Override
+ public List<PurgeableSnapshotDto> filter(List<PurgeableSnapshotDto> history) {
+ List<PurgeableSnapshotDto> result = Lists.newArrayList();
+ for (PurgeableSnapshotDto snapshot : history) {
+ if (snapshot.getDate().before(before)) {
+ result.add(snapshot);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void log() {
+ LoggerFactory.getLogger(getClass()).info("-> Delete data prior to: " + DateUtils.formatDate(before));
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filter.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filter.java
new file mode 100644
index 00000000000..ef4a3753242
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filter.java
@@ -0,0 +1,31 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.List;
+
+interface Filter {
+ List<PurgeableSnapshotDto> filter(List<PurgeableSnapshotDto> snapshots);
+
+ void log();
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filters.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filters.java
new file mode 100644
index 00000000000..8e59596212c
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filters.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.api.config.Settings;
+import org.sonar.core.computation.dbcleaner.DbCleanerConstants;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+
+class Filters {
+ private final List<Filter> all = Lists.newArrayList();
+
+ Filters(Settings settings) {
+ Date dateToStartKeepingOneSnapshotByDay = getDateFromHours(settings, DbCleanerConstants.HOURS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_DAY);
+ Date dateToStartKeepingOneSnapshotByWeek = getDateFromWeeks(settings, DbCleanerConstants.WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_WEEK);
+ Date dateToStartKeepingOneSnapshotByMonth = getDateFromWeeks(settings, DbCleanerConstants.WEEKS_BEFORE_KEEPING_ONLY_ONE_SNAPSHOT_BY_MONTH);
+ Date dateToStartDeletingAllSnapshots = getDateFromWeeks(settings, DbCleanerConstants.WEEKS_BEFORE_DELETING_ALL_SNAPSHOTS);
+
+ all.add(new KeepOneFilter(dateToStartKeepingOneSnapshotByWeek, dateToStartKeepingOneSnapshotByDay, Calendar.DAY_OF_YEAR, "day"));
+ all.add(new KeepOneFilter(dateToStartKeepingOneSnapshotByMonth, dateToStartKeepingOneSnapshotByWeek, Calendar.WEEK_OF_YEAR, "week"));
+ all.add(new KeepOneFilter(dateToStartDeletingAllSnapshots, dateToStartKeepingOneSnapshotByMonth, Calendar.MONTH, "month"));
+ all.add(new DeleteAllFilter(dateToStartDeletingAllSnapshots));
+ }
+
+ static Date getDateFromWeeks(Settings settings, String propertyKey) {
+ int weeks = settings.getInt(propertyKey);
+ return DateUtils.addWeeks(new Date(), -weeks);
+ }
+
+ static Date getDateFromHours(Settings settings, String propertyKey) {
+ int hours = settings.getInt(propertyKey);
+ return DateUtils.addHours(new Date(), -hours);
+ }
+
+ List<Filter> all() {
+ return all;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Interval.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Interval.java
new file mode 100644
index 00000000000..5676a858495
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Interval.java
@@ -0,0 +1,74 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+final class Interval {
+ List<PurgeableSnapshotDto> snapshots = Lists.newArrayList();
+
+ void add(PurgeableSnapshotDto snapshot) {
+ snapshots.add(snapshot);
+ }
+
+ List<PurgeableSnapshotDto> get() {
+ return snapshots;
+ }
+
+ int count() {
+ return snapshots.size();
+ }
+
+ static List<Interval> group(List<PurgeableSnapshotDto> snapshots, Date start, Date end, int calendarField) {
+ List<Interval> intervals = Lists.newArrayList();
+
+ GregorianCalendar calendar = new GregorianCalendar();
+ int lastYear = -1;
+ int lastFieldValue = -1;
+ Interval currentInterval = null;
+
+ for (PurgeableSnapshotDto snapshot : snapshots) {
+ if (!DateUtils.isSameDay(start, snapshot.getDate()) && snapshot.getDate().after(start) &&
+ (snapshot.getDate().before(end) || DateUtils.isSameDay(end, snapshot.getDate()))) {
+ calendar.setTime(snapshot.getDate());
+ int currentFieldValue = calendar.get(calendarField);
+ int currentYear = calendar.get(Calendar.YEAR);
+ if (lastYear!=currentYear || lastFieldValue != currentFieldValue) {
+ currentInterval = new Interval();
+ intervals.add(currentInterval);
+ }
+ lastFieldValue = currentFieldValue;
+ lastYear = currentYear;
+ if (currentInterval != null) {
+ currentInterval.add(snapshot);
+ }
+ }
+ }
+ return intervals;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilter.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilter.java
new file mode 100644
index 00000000000..419add8383f
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilter.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.Date;
+import java.util.List;
+
+class KeepOneFilter implements Filter {
+
+ private final Date start;
+ private final Date end;
+ private final int dateField;
+ private final String label;
+
+ KeepOneFilter(Date start, Date end, int calendarField, String label) {
+ this.start = start;
+ this.end = end;
+ this.dateField = calendarField;
+ this.label = label;
+ }
+
+ @Override
+ public List<PurgeableSnapshotDto> filter(List<PurgeableSnapshotDto> history) {
+ List<Interval> intervals = Interval.group(history, start, end, dateField);
+ List<PurgeableSnapshotDto> result = Lists.newArrayList();
+ for (Interval interval : intervals) {
+ appendSnapshotsToDelete(interval, result);
+ }
+
+ return result;
+ }
+
+ @Override
+ public void log() {
+ LoggerFactory.getLogger(getClass()).info("-> Keep one snapshot per " + label + " between " + DateUtils.formatDate(start) + " and " + DateUtils.formatDate(end));
+ }
+
+ private void appendSnapshotsToDelete(Interval interval, List<PurgeableSnapshotDto> toDelete) {
+ if (interval.count() > 1) {
+ List<PurgeableSnapshotDto> deletables = Lists.newArrayList();
+ List<PurgeableSnapshotDto> toKeep = Lists.newArrayList();
+ for (PurgeableSnapshotDto snapshot : interval.get()) {
+ if (isDeletable(snapshot)) {
+ deletables.add(snapshot);
+ } else {
+ toKeep.add(snapshot);
+ }
+ }
+
+ if (!toKeep.isEmpty()) {
+ toDelete.addAll(deletables);
+
+ } else if (deletables.size() > 1) {
+ // keep one snapshot
+ toDelete.addAll(deletables.subList(1, deletables.size()));
+ }
+ }
+ }
+
+ @VisibleForTesting
+ static boolean isDeletable(PurgeableSnapshotDto snapshot) {
+ return !snapshot.isLast() && !snapshot.hasEvents();
+ }
+
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/package-info.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/package-info.java
new file mode 100644
index 00000000000..283ef7fa912
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java b/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
index d98fddab931..b678394d38a 100644
--- a/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
+++ b/sonar-core/src/main/java/org/sonar/core/config/CorePropertyDefinitions.java
@@ -24,6 +24,7 @@ import com.google.common.collect.Lists;
import org.sonar.api.CoreProperties;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.resources.Qualifiers;
+import org.sonar.core.computation.dbcleaner.DbCleanerProperties;
import java.util.List;
@@ -39,6 +40,7 @@ public class CorePropertyDefinitions {
defs.addAll(ExclusionProperties.all());
defs.addAll(SecurityProperties.all());
defs.addAll(DebtProperties.all());
+ defs.addAll(DbCleanerProperties.all());
defs.addAll(ImmutableList.of(
// BATCH
@@ -101,7 +103,7 @@ public class CorePropertyDefinitions {
.category(CoreProperties.CATEGORY_GENERAL)
.subCategory(CoreProperties.SUBCATEGORY_DIFFERENTIAL_VIEWS)
.build()
- ));
+ ));
return defs;
}
}
diff --git a/sonar-core/src/main/resources/org/sonar/core/computation/db/AnalysisReportMapper.xml b/sonar-core/src/main/resources/org/sonar/core/computation/db/AnalysisReportMapper.xml
index e2166b7b36f..16613e4a946 100644
--- a/sonar-core/src/main/resources/org/sonar/core/computation/db/AnalysisReportMapper.xml
+++ b/sonar-core/src/main/resources/org/sonar/core/computation/db/AnalysisReportMapper.xml
@@ -6,7 +6,6 @@
<!-- the data report is not brought back by default as it could be too big in memory -->
ar.id,
ar.project_key as projectKey,
- ar.project_name as projectName,
ar.report_status as status,
ar.snapshot_id as snapshotId,
ar.created_at as createdAt,
@@ -17,9 +16,9 @@
<insert id="insert" parameterType="AnalysisReport" useGeneratedKeys="true">
insert into analysis_reports
- (project_key, project_name, snapshot_id, report_status, report_data, created_at, updated_at, started_at,
+ (project_key, snapshot_id, report_status, report_data, created_at, updated_at, started_at,
finished_at)
- values (#{projectKey}, #{projectName}, #{snapshotId}, #{status}, #{data}, #{createdAt}, #{updatedAt}, #{startedAt},
+ values (#{projectKey}, #{snapshotId}, #{status}, #{data}, #{createdAt}, #{updatedAt}, #{startedAt},
#{finishedAt})
</insert>
diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerPropertiesTest.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerPropertiesTest.java
new file mode 100644
index 00000000000..4c4311aac3b
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerPropertiesTest.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner;
+
+import org.junit.Test;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DbCleanerPropertiesTest {
+
+ @Test
+ public void shouldGetExtensions() {
+ assertThat(new DbCleanerProperties().all()).hasSize(6);
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerTestUtils.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerTestUtils.java
new file mode 100644
index 00000000000..392e58c0f0b
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DbCleanerTestUtils.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner;
+
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+public final class DbCleanerTestUtils {
+
+ private DbCleanerTestUtils() {
+ }
+
+ public static PurgeableSnapshotDto createSnapshotWithDate(long snapshotId, String date) {
+ PurgeableSnapshotDto snapshot = new PurgeableSnapshotDto();
+ snapshot.setSnapshotId(snapshotId);
+ snapshot.setDate(DateUtils.parseDate(date));
+ return snapshot;
+ }
+
+ public static PurgeableSnapshotDto createSnapshotWithDateTime(long snapshotId, String datetime) {
+ PurgeableSnapshotDto snapshot = new PurgeableSnapshotDto();
+ snapshot.setSnapshotId(snapshotId);
+ snapshot.setDate(DateUtils.parseDateTime(datetime));
+ return snapshot;
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTaskTest.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTaskTest.java
new file mode 100644
index 00000000000..72359ee3dee
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTaskTest.java
@@ -0,0 +1,106 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner;
+
+import ch.qos.logback.classic.Logger;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.config.PropertyDefinitions;
+import org.sonar.api.config.Settings;
+import org.sonar.api.resources.Scopes;
+import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner;
+import org.sonar.core.purge.PurgeConfiguration;
+import org.sonar.core.purge.PurgeDao;
+import org.sonar.core.purge.PurgeProfiler;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+
+public class DefaultPurgeTaskTest {
+
+ @Test
+ public void shouldNotDeleteHistoricalDataOfDirectories() {
+ PurgeDao purgeDao = mock(PurgeDao.class);
+ Settings settings = new Settings(new PropertyDefinitions(DbCleanerProperties.all()));
+ settings.setProperty(DbCleanerConstants.PROPERTY_CLEAN_DIRECTORY, "false");
+ DefaultPurgeTask task = new DefaultPurgeTask(purgeDao, settings, mock(DefaultPeriodCleaner.class), mock(PurgeProfiler.class));
+
+ task.purge(1L);
+
+ verify(purgeDao).purge(argThat(new ArgumentMatcher<PurgeConfiguration>() {
+ @Override
+ public boolean matches(Object o) {
+ PurgeConfiguration conf = (PurgeConfiguration) o;
+ return conf.rootProjectId() == 1L && conf.scopesWithoutHistoricalData().length == 1 && conf.scopesWithoutHistoricalData()[0].equals(Scopes.FILE);
+ }
+ }));
+ }
+
+ @Test
+ public void shouldDeleteHistoricalDataOfDirectoriesByDefault() {
+ PurgeDao purgeDao = mock(PurgeDao.class);
+ Settings settings = new Settings(new PropertyDefinitions(DbCleanerProperties.all()));
+ DefaultPurgeTask task = new DefaultPurgeTask(purgeDao, settings, mock(DefaultPeriodCleaner.class), mock(PurgeProfiler.class));
+
+ task.purge(1L);
+
+ verify(purgeDao).purge(argThat(new ArgumentMatcher<PurgeConfiguration>() {
+ @Override
+ public boolean matches(Object o) {
+ PurgeConfiguration conf = (PurgeConfiguration) o;
+ return conf.rootProjectId() == 1L &&
+ conf.scopesWithoutHistoricalData().length == 2 &&
+ conf.scopesWithoutHistoricalData()[0].equals(Scopes.DIRECTORY) &&
+ conf.scopesWithoutHistoricalData()[1].equals(Scopes.FILE);
+ }
+ }));
+ }
+
+ @Test
+ public void shouldNotFailOnErrors() {
+ PurgeDao purgeDao = mock(PurgeDao.class);
+ when(purgeDao.purge(any(PurgeConfiguration.class))).thenThrow(new RuntimeException());
+ DefaultPurgeTask task = new DefaultPurgeTask(purgeDao, new Settings(), mock(DefaultPeriodCleaner.class), mock(PurgeProfiler.class));
+
+ task.purge(1L);
+
+ verify(purgeDao, times(1)).purge(any(PurgeConfiguration.class));
+ }
+
+ @Test
+ public void shouldDumpProfiling() {
+ PurgeConfiguration conf = new PurgeConfiguration(1L, new String[0], 30);
+ PurgeDao purgeDao = mock(PurgeDao.class);
+ when(purgeDao.purge(conf)).thenThrow(new RuntimeException());
+ Settings settings = new Settings(new PropertyDefinitions(DbCleanerProperties.all()));
+ settings.setProperty(CoreProperties.PROFILING_LOG_PROPERTY, true);
+ PurgeProfiler profiler = mock(PurgeProfiler.class);
+
+ DefaultPurgeTask task = new DefaultPurgeTask(purgeDao, settings, mock(DefaultPeriodCleaner.class), profiler);
+ task.purge(1L);
+
+ verify(profiler).dump(anyLong(), any(Logger.class));
+ }
+
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleanerTest.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleanerTest.java
new file mode 100644
index 00000000000..0a5872041d2
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleanerTest.java
@@ -0,0 +1,90 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import org.apache.commons.lang.ObjectUtils;
+import org.hamcrest.BaseMatcher;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.sonar.api.config.Settings;
+import org.sonar.core.purge.PurgeDao;
+import org.sonar.core.purge.PurgeSnapshotQuery;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+
+public class DefaultPeriodCleanerTest {
+
+
+ @Test
+ public void doClean() {
+ PurgeDao dao = mock(PurgeDao.class);
+ when(dao.selectPurgeableSnapshots(123L)).thenReturn(Arrays.asList(
+ new PurgeableSnapshotDto().setSnapshotId(999L).setDate(new Date())));
+ Filter filter1 = newLazyFilter();
+ Filter filter2 = newLazyFilter();
+
+ DefaultPeriodCleaner cleaner = new DefaultPeriodCleaner(dao, mock(Settings.class));
+ cleaner.doClean(123L, Arrays.asList(filter1, filter2));
+
+ verify(filter1).log();
+ verify(filter2).log();
+ verify(dao, times(2)).deleteSnapshots(argThat(newRootSnapshotQuery()));
+ verify(dao, times(2)).deleteSnapshots(argThat(newSnapshotIdQuery()));
+ }
+
+ private BaseMatcher<PurgeSnapshotQuery> newRootSnapshotQuery() {
+ return new ArgumentMatcher<PurgeSnapshotQuery>() {
+ @Override
+ public boolean matches(Object o) {
+ PurgeSnapshotQuery query = (PurgeSnapshotQuery) o;
+ return ObjectUtils.equals(query.getRootSnapshotId(), 999L);
+ }
+ };
+ }
+
+ private BaseMatcher<PurgeSnapshotQuery> newSnapshotIdQuery() {
+ return new ArgumentMatcher<PurgeSnapshotQuery>() {
+ @Override
+ public boolean matches(Object o) {
+ PurgeSnapshotQuery query = (PurgeSnapshotQuery) o;
+ return ObjectUtils.equals(query.getId(), 999L);
+ }
+ };
+ }
+
+ private Filter newLazyFilter() {
+ Filter filter1 = mock(Filter.class);
+ when(filter1.filter(anyListOf(PurgeableSnapshotDto.class))).thenAnswer(new Answer<Object>() {
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ return invocation.getArguments()[0];
+ }
+ });
+ return filter1;
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilterTest.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilterTest.java
new file mode 100644
index 00000000000..2a84512355e
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilterTest.java
@@ -0,0 +1,47 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.computation.dbcleaner.DbCleanerTestUtils;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class DeleteAllFilterTest {
+
+ @Test
+ public void shouldDeleteAllSnapshotsPriorToDate() {
+ Filter filter = new DeleteAllFilter(DateUtils.parseDate("2011-12-25"));
+
+ List<PurgeableSnapshotDto> toDelete = filter.filter(Arrays.asList(
+ DbCleanerTestUtils.createSnapshotWithDate(1L, "2010-01-01"),
+ DbCleanerTestUtils.createSnapshotWithDate(2L, "2010-12-25"),
+ DbCleanerTestUtils.createSnapshotWithDate(3L, "2012-01-01")
+ ));
+
+ assertThat(toDelete).onProperty("snapshotId").containsOnly(1L, 2L);
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/IntervalTest.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/IntervalTest.java
new file mode 100644
index 00000000000..a9c5aecc698
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/IntervalTest.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.sonar.core.computation.dbcleaner.DbCleanerTestUtils.createSnapshotWithDate;
+import static org.sonar.core.computation.dbcleaner.DbCleanerTestUtils.createSnapshotWithDateTime;
+
+public class IntervalTest {
+ static int calendarField(Interval interval, int field) {
+ if (interval.count() == 0) {
+ return -1;
+ }
+
+ PurgeableSnapshotDto first = interval.get().iterator().next();
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTime(first.getDate());
+ return cal.get(field);
+ }
+
+ @Test
+ public void shouldGroupByIntervals() {
+ List<PurgeableSnapshotDto> snapshots = Arrays.asList(
+ createSnapshotWithDate(1L, "2011-04-03"),
+
+ createSnapshotWithDate(2L, "2011-05-01"),
+ createSnapshotWithDate(3L, "2011-05-19"),
+
+ createSnapshotWithDate(4L, "2011-06-02"),
+ createSnapshotWithDate(5L, "2011-06-20"),
+
+ createSnapshotWithDate(6L, "2012-06-29") // out of scope
+ );
+
+ List<Interval> intervals = Interval.group(snapshots, DateUtils.parseDate("2010-01-01"), DateUtils.parseDate("2011-12-31"), Calendar.MONTH);
+ assertThat(intervals.size(), is(3));
+
+ assertThat(intervals.get(0).count(), is(1));
+ assertThat(calendarField(intervals.get(0), Calendar.MONTH), is(Calendar.APRIL));
+
+ assertThat(intervals.get(1).count(), is(2));
+ assertThat(calendarField(intervals.get(1), Calendar.MONTH), is(Calendar.MAY));
+
+ assertThat(intervals.get(2).count(), is(2));
+ assertThat(calendarField(intervals.get(2), Calendar.MONTH), is(Calendar.JUNE));
+ }
+
+ @Test
+ public void shouldNotJoinMonthsOfDifferentYears() {
+ List<PurgeableSnapshotDto> snapshots = Arrays.asList(
+ createSnapshotWithDate(1L, "2010-04-03"),
+ createSnapshotWithDate(2L, "2011-04-13")
+ );
+
+ List<Interval> intervals = Interval.group(snapshots, DateUtils.parseDate("2010-01-01"), DateUtils.parseDate("2011-12-31"), Calendar.MONTH);
+ assertThat(intervals.size(), is(2));
+
+ assertThat(intervals.get(0).count(), is(1));
+ assertThat(calendarField(intervals.get(0), Calendar.MONTH), is(Calendar.APRIL));
+ assertThat(calendarField(intervals.get(0), Calendar.YEAR), is(2010));
+
+ assertThat(intervals.get(1).count(), is(1));
+ assertThat(calendarField(intervals.get(1), Calendar.MONTH), is(Calendar.APRIL));
+ assertThat(calendarField(intervals.get(1), Calendar.YEAR), is(2011));
+ }
+
+ @Test
+ public void shouldIgnoreTimeWhenGroupingByIntervals() {
+ List<PurgeableSnapshotDto> snapshots = Arrays.asList(
+ createSnapshotWithDateTime(1L, "2011-05-25T16:16:48+0100"),
+ createSnapshotWithDateTime(2L, "2012-01-26T16:16:48+0100"),
+ createSnapshotWithDateTime(3L, "2012-01-27T16:16:48+0100")
+ );
+
+ List<Interval> intervals = Interval.group(snapshots, DateUtils.parseDate("2011-05-25"), DateUtils.parseDate("2012-01-26"), Calendar.MONTH);
+ assertThat(intervals.size(), is(1));
+ assertThat(intervals.get(0).count(), is(1));
+ assertThat(intervals.get(0).get().get(0).getSnapshotId(), is(2L));
+ }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilterTest.java b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilterTest.java
new file mode 100644
index 00000000000..8e855f987df
--- /dev/null
+++ b/sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilterTest.java
@@ -0,0 +1,95 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 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.computation.dbcleaner.period;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.core.purge.PurgeableSnapshotDto;
+
+import javax.annotation.Nullable;
+
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.sonar.core.computation.dbcleaner.DbCleanerTestUtils.createSnapshotWithDate;
+
+public class KeepOneFilterTest {
+
+ private static List<Long> snapshotIds(List<PurgeableSnapshotDto> snapshotDtos) {
+ return newArrayList(Iterables.transform(snapshotDtos, new Function<PurgeableSnapshotDto, Long>() {
+ @Override
+ public Long apply(@Nullable PurgeableSnapshotDto input) {
+ return input != null ? input.getSnapshotId() : null;
+ }
+ }));
+ }
+
+ @Test
+ public void shouldOnlyOneSnapshotPerInterval() {
+ Filter filter = new KeepOneFilter(DateUtils.parseDate("2011-03-25"), DateUtils.parseDate("2011-08-25"), Calendar.MONTH, "month");
+
+ List<PurgeableSnapshotDto> toDelete = filter.filter(Arrays.<PurgeableSnapshotDto>asList(
+ createSnapshotWithDate(1L, "2010-01-01"), // out of scope -> keep
+ createSnapshotWithDate(2L, "2011-05-01"), // may -> keep
+ createSnapshotWithDate(3L, "2011-05-02"), // may -> to be deleted
+ createSnapshotWithDate(4L, "2011-05-19"), // may -> to be deleted
+ createSnapshotWithDate(5L, "2011-06-01"), // june -> keep
+ createSnapshotWithDate(6L, "2012-01-01") // out of scope -> keep
+ ));
+
+ assertThat(toDelete).hasSize(2);
+
+ List<Long> snapshotIds = snapshotIds(toDelete);
+ assertThat(snapshotIds).contains(3L);
+ assertThat(snapshotIds.contains(4L));
+ }
+
+ @Test
+ public void shouldKeepNonDeletableSnapshots() {
+ Filter filter = new KeepOneFilter(DateUtils.parseDate("2011-03-25"), DateUtils.parseDate("2011-08-25"), Calendar.MONTH, "month");
+
+ List<PurgeableSnapshotDto> toDelete = filter.filter(Arrays.<PurgeableSnapshotDto>asList(
+ createSnapshotWithDate(1L, "2011-05-01"), // to be deleted
+ createSnapshotWithDate(2L, "2011-05-02").setLast(true),
+ createSnapshotWithDate(3L, "2011-05-19").setHasEvents(true).setLast(false),
+ createSnapshotWithDate(4L, "2011-05-23") // to be deleted
+ ));
+
+ assertThat(toDelete).hasSize(2);
+
+ List<Long> snapshotIds = snapshotIds(toDelete);
+ assertThat(snapshotIds).contains(1L);
+ assertThat(snapshotIds.contains(4L));
+ }
+
+ @Test
+ public void test_isDeletable() {
+ assertThat(KeepOneFilter.isDeletable(createSnapshotWithDate(1L, "2011-05-01"))).isTrue();
+ assertThat(KeepOneFilter.isDeletable(createSnapshotWithDate(1L, "2011-05-01").setLast(true))).isFalse();
+ assertThat(KeepOneFilter.isDeletable(createSnapshotWithDate(1L, "2011-05-01").setHasEvents(true))).isFalse();
+ }
+
+}