From 1c6c5ac69aed0f7dd35f5007105ed9ac6f1f568d Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 3 Dec 2014 17:34:21 +0100 Subject: compute engine – move ProjectPurgeTask on the server side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dbcleaner/DataCleanerProperties.java | 108 -------------------- .../computation/dbcleaner/DbCleanerConstants.java | 34 ------- .../computation/dbcleaner/DefaultPurgeTask.java | 111 --------------------- .../computation/dbcleaner/ProjectPurgeTask.java | 84 ---------------- .../core/computation/dbcleaner/package-info.java | 24 ----- .../dbcleaner/period/DefaultPeriodCleaner.java | 88 ---------------- .../dbcleaner/period/DeleteAllFilter.java | 53 ---------- .../core/computation/dbcleaner/period/Filter.java | 31 ------ .../core/computation/dbcleaner/period/Filters.java | 60 ----------- .../computation/dbcleaner/period/Interval.java | 74 -------------- .../dbcleaner/period/KeepOneFilter.java | 89 ----------------- .../computation/dbcleaner/period/package-info.java | 24 ----- .../sonar/core/config/CorePropertyDefinitions.java | 2 +- .../org/sonar/core/purge/PurgeConfiguration.java | 2 +- .../plugins/dbcleaner/api/DbCleanerConstants.java | 2 +- .../dbcleaner/DataCleanerProperties.java | 108 ++++++++++++++++++++ .../computation/dbcleaner/DbCleanerConstants.java | 34 +++++++ .../computation/dbcleaner/DefaultPurgeTask.java | 111 +++++++++++++++++++++ .../server/computation/dbcleaner/package-info.java | 24 +++++ .../dbcleaner/period/DefaultPeriodCleaner.java | 88 ++++++++++++++++ .../dbcleaner/period/DeleteAllFilter.java | 53 ++++++++++ .../computation/dbcleaner/period/Filter.java | 31 ++++++ .../computation/dbcleaner/period/Filters.java | 60 +++++++++++ .../computation/dbcleaner/period/Interval.java | 74 ++++++++++++++ .../dbcleaner/period/KeepOneFilter.java | 89 +++++++++++++++++ .../computation/dbcleaner/period/package-info.java | 24 +++++ 26 files changed, 699 insertions(+), 783 deletions(-) delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DataCleanerProperties.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerConstants.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/package-info.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilter.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filter.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filters.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Interval.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilter.java delete mode 100644 sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/package-info.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DataCleanerProperties.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DbCleanerConstants.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DefaultPurgeTask.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/package-info.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/DefaultPeriodCleaner.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/DeleteAllFilter.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Filter.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Filters.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Interval.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/KeepOneFilter.java create mode 100644 sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/package-info.java (limited to 'sonar-core/src/main') diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DataCleanerProperties.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DataCleanerProperties.java deleted file mode 100644 index f4bc98dea9a..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DataCleanerProperties.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 DataCleanerProperties { - - private DataCleanerProperties() { - } - - public static List 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/DbCleanerConstants.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerConstants.java deleted file mode 100644 index a577f632608..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DbCleanerConstants.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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/DefaultPurgeTask.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java deleted file mode 100644 index 2e0f073571e..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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 com.google.common.annotations.VisibleForTesting; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.CoreProperties; -import org.sonar.api.config.Settings; -import org.sonar.api.resources.Qualifiers; -import org.sonar.api.utils.TimeUtils; -import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; -import org.sonar.core.purge.IdUuidPair; -import org.sonar.core.purge.PurgeConfiguration; -import org.sonar.core.purge.PurgeDao; -import org.sonar.core.purge.PurgeProfiler; -import org.sonar.core.resource.ResourceDao; -import org.sonar.core.resource.ResourceDto; -import org.sonar.plugins.dbcleaner.api.PurgeTask; - -import static org.sonar.core.purge.PurgeConfiguration.newDefaultPurgeConfiguration; - -/** - * @since 2.14 - */ -public class DefaultPurgeTask implements PurgeTask { - private static final Logger LOG = LoggerFactory.getLogger(DefaultPurgeTask.class); - private final PurgeProfiler profiler; - private final PurgeDao purgeDao; - private final ResourceDao resourceDao; - private final Settings settings; - private final DefaultPeriodCleaner periodCleaner; - - public DefaultPurgeTask(PurgeDao purgeDao, ResourceDao resourceDao, Settings settings, DefaultPeriodCleaner periodCleaner, PurgeProfiler profiler) { - this.purgeDao = purgeDao; - this.resourceDao = resourceDao; - this.settings = settings; - this.periodCleaner = periodCleaner; - this.profiler = profiler; - } - - @Override - public DefaultPurgeTask delete(long resourceId) { - ResourceDto project = resourceDao.getResource(resourceId); - purgeDao.deleteResourceTree(new IdUuidPair(project.getId(), project.getUuid())); - return this; - } - - @VisibleForTesting - boolean isNotViewNorSubview(String resourceQualifier) { - return !(Qualifiers.VIEW.equals(resourceQualifier) || Qualifiers.SUBVIEW.equals(resourceQualifier)); - } - - @Override - public DefaultPurgeTask purge(long resourceId) { - long start = System.currentTimeMillis(); - String resourceQualifier = resourceDao.getResource(resourceId).getQualifier(); - if (isNotViewNorSubview(resourceQualifier)) { - 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(newPurgeConfigurationOnResource(resourceId)); - } catch (Exception e) { - // purge errors must no fail the report analysis - LOG.error("Fail to purge data [id=" + resourceId + "]", e); - } - } - - public PurgeConfiguration newPurgeConfigurationOnResource(long resourceId) { - return newDefaultPurgeConfiguration(settings, resourceId); - } -} diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java deleted file mode 100644 index 931a746b620..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/ProjectPurgeTask.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.ServerComponent; -import org.sonar.api.config.Settings; -import org.sonar.api.utils.TimeUtils; -import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.purge.PurgeConfiguration; -import org.sonar.core.purge.PurgeDao; -import org.sonar.core.purge.PurgeProfiler; - -import java.util.List; - -public class ProjectPurgeTask implements ServerComponent { - private static final Logger LOG = LoggerFactory.getLogger(ProjectPurgeTask.class); - private final PurgeProfiler profiler; - private final PurgeDao purgeDao; - private final DefaultPeriodCleaner periodCleaner; - - public ProjectPurgeTask(PurgeDao purgeDao, DefaultPeriodCleaner periodCleaner, PurgeProfiler profiler) { - this.purgeDao = purgeDao; - this.periodCleaner = periodCleaner; - this.profiler = profiler; - } - - public ProjectPurgeTask purge(DbSession session, PurgeConfiguration configuration, Settings settings) { - long start = System.currentTimeMillis(); - profiler.reset(); - cleanHistoricalData(session, configuration.rootProjectId(), settings); - doPurge(session, configuration); - 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(DbSession session, long resourceId, Settings settings) { - try { - periodCleaner.clean(session, resourceId, settings); - } catch (Exception e) { - // purge errors must no fail the batch - LOG.error("Fail to clean historical data [id=" + resourceId + "]", e); - } - } - - private void doPurge(DbSession session, PurgeConfiguration configuration) { - try { - purgeDao.purge(session, configuration); - } catch (Exception e) { - // purge errors must no fail the report analysis - LOG.error("Fail to purge data [id=" + configuration.rootProjectId() + "]", e); - } - } - - public List findUuidsToDisable(DbSession session, Long projectId) { - return purgeDao.selectPurgeableFiles(session, projectId); - } -} diff --git a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/package-info.java b/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/package-info.java deleted file mode 100644 index 8a7156fd308..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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; - -import javax.annotation.ParametersAreNonnullByDefault; 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 deleted file mode 100644 index e87214d4e0e..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DefaultPeriodCleaner.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.ServerExtension; -import org.sonar.api.config.Settings; -import org.sonar.api.task.TaskExtension; -import org.sonar.api.utils.DateUtils; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; -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, ServerExtension { - - private static final Logger LOG = LoggerFactory.getLogger(DefaultPeriodCleaner.class); - private PurgeDao purgeDao; - private Settings settings; - private MyBatis mybatis; - - public DefaultPeriodCleaner(PurgeDao purgeDao, Settings settings, MyBatis mybatis) { - this.purgeDao = purgeDao; - this.settings = settings; - this.mybatis = mybatis; - } - - public void clean(long projectId) { - clean(projectId, settings); - } - - public void clean(long projectId, Settings settings) { - DbSession session = mybatis.openSession(true); - try { - doClean(projectId, new Filters(settings).all(), session); - } finally { - MyBatis.closeQuietly(session); - } - } - - public void clean(DbSession session, long projectId, Settings settings) { - doClean(projectId, new Filters(settings).all(), session); - } - - @VisibleForTesting - void doClean(long projectId, List filters, DbSession session) { - List history = selectProjectSnapshots(projectId, session); - for (Filter filter : filters) { - filter.log(); - delete(filter.filter(history), session); - } - } - - private void delete(List snapshots, DbSession session) { - for (PurgeableSnapshotDto snapshot : snapshots) { - LOG.info("<- Delete snapshot: " + DateUtils.formatDateTime(snapshot.getDate()) + " [" + snapshot.getSnapshotId() + "]"); - purgeDao.deleteSnapshots(PurgeSnapshotQuery.create().setRootSnapshotId(snapshot.getSnapshotId()), session); - purgeDao.deleteSnapshots(PurgeSnapshotQuery.create().setId(snapshot.getSnapshotId()), session); - } - } - - private List selectProjectSnapshots(long resourceId, DbSession session) { - return purgeDao.selectPurgeableSnapshots(resourceId, session); - } -} 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 deleted file mode 100644 index 25dcb409ea1..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/DeleteAllFilter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 filter(List history) { - List 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 deleted file mode 100644 index ef4a3753242..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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 filter(List 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 deleted file mode 100644 index 8e59596212c..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Filters.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 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 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 deleted file mode 100644 index 5676a858495..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/Interval.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 snapshots = Lists.newArrayList(); - - void add(PurgeableSnapshotDto snapshot) { - snapshots.add(snapshot); - } - - List get() { - return snapshots; - } - - int count() { - return snapshots.size(); - } - - static List group(List snapshots, Date start, Date end, int calendarField) { - List 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 deleted file mode 100644 index 419add8383f..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/KeepOneFilter.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 filter(List history) { - List intervals = Interval.group(history, start, end, dateField); - List 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 toDelete) { - if (interval.count() > 1) { - List deletables = Lists.newArrayList(); - List 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 deleted file mode 100644 index 283ef7fa912..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/period/package-info.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 d10afcf4cf9..0bb4bb69e0a 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,7 +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.DataCleanerProperties; +import org.sonar.server.computation.dbcleaner.DataCleanerProperties; import java.util.List; diff --git a/sonar-core/src/main/java/org/sonar/core/purge/PurgeConfiguration.java b/sonar-core/src/main/java/org/sonar/core/purge/PurgeConfiguration.java index d3fc9c1c4bd..37886b30732 100644 --- a/sonar-core/src/main/java/org/sonar/core/purge/PurgeConfiguration.java +++ b/sonar-core/src/main/java/org/sonar/core/purge/PurgeConfiguration.java @@ -24,7 +24,7 @@ import org.apache.commons.lang.time.DateUtils; import org.sonar.api.config.Settings; import org.sonar.api.resources.Scopes; import org.sonar.api.utils.System2; -import org.sonar.core.computation.dbcleaner.DbCleanerConstants; +import org.sonar.server.computation.dbcleaner.DbCleanerConstants; import javax.annotation.CheckForNull; diff --git a/sonar-core/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerConstants.java b/sonar-core/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerConstants.java index 8cad98c10c5..53fc44ede3a 100644 --- a/sonar-core/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerConstants.java +++ b/sonar-core/src/main/java/org/sonar/plugins/dbcleaner/api/DbCleanerConstants.java @@ -23,5 +23,5 @@ package org.sonar.plugins.dbcleaner.api; * @deprecated (is used by the plugin views only. Will rely on the implementation when views analysis views moved on the server side) */ @Deprecated -public interface DbCleanerConstants extends org.sonar.core.computation.dbcleaner.DbCleanerConstants { +public interface DbCleanerConstants extends org.sonar.server.computation.dbcleaner.DbCleanerConstants { } diff --git a/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DataCleanerProperties.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DataCleanerProperties.java new file mode 100644 index 00000000000..902a36350a3 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DataCleanerProperties.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.server.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 DataCleanerProperties { + + private DataCleanerProperties() { + } + + public static List 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/server/computation/dbcleaner/DbCleanerConstants.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DbCleanerConstants.java new file mode 100644 index 00000000000..fbd853e00bf --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/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.server.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/server/computation/dbcleaner/DefaultPurgeTask.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DefaultPurgeTask.java new file mode 100644 index 00000000000..188164e55fc --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/DefaultPurgeTask.java @@ -0,0 +1,111 @@ +/* + * 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.server.computation.dbcleaner; + +import com.google.common.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.CoreProperties; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.utils.TimeUtils; +import org.sonar.server.computation.dbcleaner.period.DefaultPeriodCleaner; +import org.sonar.core.purge.IdUuidPair; +import org.sonar.core.purge.PurgeConfiguration; +import org.sonar.core.purge.PurgeDao; +import org.sonar.core.purge.PurgeProfiler; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; +import org.sonar.plugins.dbcleaner.api.PurgeTask; + +import static org.sonar.core.purge.PurgeConfiguration.newDefaultPurgeConfiguration; + +/** + * @since 2.14 + */ +public class DefaultPurgeTask implements PurgeTask { + private static final Logger LOG = LoggerFactory.getLogger(DefaultPurgeTask.class); + private final PurgeProfiler profiler; + private final PurgeDao purgeDao; + private final ResourceDao resourceDao; + private final Settings settings; + private final DefaultPeriodCleaner periodCleaner; + + public DefaultPurgeTask(PurgeDao purgeDao, ResourceDao resourceDao, Settings settings, DefaultPeriodCleaner periodCleaner, PurgeProfiler profiler) { + this.purgeDao = purgeDao; + this.resourceDao = resourceDao; + this.settings = settings; + this.periodCleaner = periodCleaner; + this.profiler = profiler; + } + + @Override + public DefaultPurgeTask delete(long resourceId) { + ResourceDto project = resourceDao.getResource(resourceId); + purgeDao.deleteResourceTree(new IdUuidPair(project.getId(), project.getUuid())); + return this; + } + + @VisibleForTesting + boolean isNotViewNorSubview(String resourceQualifier) { + return !(Qualifiers.VIEW.equals(resourceQualifier) || Qualifiers.SUBVIEW.equals(resourceQualifier)); + } + + @Override + public DefaultPurgeTask purge(long resourceId) { + long start = System.currentTimeMillis(); + String resourceQualifier = resourceDao.getResource(resourceId).getQualifier(); + if (isNotViewNorSubview(resourceQualifier)) { + 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(newPurgeConfigurationOnResource(resourceId)); + } catch (Exception e) { + // purge errors must no fail the report analysis + LOG.error("Fail to purge data [id=" + resourceId + "]", e); + } + } + + public PurgeConfiguration newPurgeConfigurationOnResource(long resourceId) { + return newDefaultPurgeConfiguration(settings, resourceId); + } +} diff --git a/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/package-info.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/package-info.java new file mode 100644 index 00000000000..f3a8c77da99 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/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.server.computation.dbcleaner; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/DefaultPeriodCleaner.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/DefaultPeriodCleaner.java new file mode 100644 index 00000000000..0fcf5236ebf --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/DefaultPeriodCleaner.java @@ -0,0 +1,88 @@ +/* + * 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.server.computation.dbcleaner.period; + +import com.google.common.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.ServerExtension; +import org.sonar.api.config.Settings; +import org.sonar.api.task.TaskExtension; +import org.sonar.api.utils.DateUtils; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +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, ServerExtension { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultPeriodCleaner.class); + private PurgeDao purgeDao; + private Settings settings; + private MyBatis mybatis; + + public DefaultPeriodCleaner(PurgeDao purgeDao, Settings settings, MyBatis mybatis) { + this.purgeDao = purgeDao; + this.settings = settings; + this.mybatis = mybatis; + } + + public void clean(long projectId) { + clean(projectId, settings); + } + + public void clean(long projectId, Settings settings) { + DbSession session = mybatis.openSession(true); + try { + doClean(projectId, new Filters(settings).all(), session); + } finally { + MyBatis.closeQuietly(session); + } + } + + public void clean(DbSession session, long projectId, Settings settings) { + doClean(projectId, new Filters(settings).all(), session); + } + + @VisibleForTesting + void doClean(long projectId, List filters, DbSession session) { + List history = selectProjectSnapshots(projectId, session); + for (Filter filter : filters) { + filter.log(); + delete(filter.filter(history), session); + } + } + + private void delete(List snapshots, DbSession session) { + for (PurgeableSnapshotDto snapshot : snapshots) { + LOG.info("<- Delete snapshot: " + DateUtils.formatDateTime(snapshot.getDate()) + " [" + snapshot.getSnapshotId() + "]"); + purgeDao.deleteSnapshots(PurgeSnapshotQuery.create().setRootSnapshotId(snapshot.getSnapshotId()), session); + purgeDao.deleteSnapshots(PurgeSnapshotQuery.create().setId(snapshot.getSnapshotId()), session); + } + } + + private List selectProjectSnapshots(long resourceId, DbSession session) { + return purgeDao.selectPurgeableSnapshots(resourceId, session); + } +} diff --git a/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/DeleteAllFilter.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/DeleteAllFilter.java new file mode 100644 index 00000000000..83c8a9edf4b --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/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.server.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 filter(List history) { + List 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/server/computation/dbcleaner/period/Filter.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Filter.java new file mode 100644 index 00000000000..c4bf00a1bd6 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/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.server.computation.dbcleaner.period; + +import org.sonar.core.purge.PurgeableSnapshotDto; + +import java.util.List; + +interface Filter { + List filter(List snapshots); + + void log(); +} diff --git a/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Filters.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Filters.java new file mode 100644 index 00000000000..929316c0a2b --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/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.server.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.server.computation.dbcleaner.DbCleanerConstants; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +class Filters { + private final List 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 all() { + return all; + } +} diff --git a/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Interval.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/Interval.java new file mode 100644 index 00000000000..a4ef804274b --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/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.server.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 snapshots = Lists.newArrayList(); + + void add(PurgeableSnapshotDto snapshot) { + snapshots.add(snapshot); + } + + List get() { + return snapshots; + } + + int count() { + return snapshots.size(); + } + + static List group(List snapshots, Date start, Date end, int calendarField) { + List 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/server/computation/dbcleaner/period/KeepOneFilter.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/KeepOneFilter.java new file mode 100644 index 00000000000..32bda2cca08 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/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.server.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 filter(List history) { + List intervals = Interval.group(history, start, end, dateField); + List 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 toDelete) { + if (interval.count() > 1) { + List deletables = Lists.newArrayList(); + List 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/server/computation/dbcleaner/period/package-info.java b/sonar-core/src/main/java/org/sonar/server/computation/dbcleaner/period/package-info.java new file mode 100644 index 00000000000..1f547e88740 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/server/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.server.computation.dbcleaner.period; + +import javax.annotation.ParametersAreNonnullByDefault; -- cgit v1.2.3