/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program 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.
*
* This program 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.db.purge;
import com.google.common.collect.Lists;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDao;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTreeQuery;
import org.sonar.db.component.ComponentTreeQuery.Strategy;
import static java.util.Collections.emptyList;
import static org.sonar.api.utils.DateUtils.dateToLong;
import static org.sonar.db.DatabaseUtils.executeLargeInputs;
/**
* @since 2.14
*/
public class PurgeDao implements Dao {
private static final Logger LOG = Loggers.get(PurgeDao.class);
private static final String[] UNPROCESSED_STATUS = new String[] {"U"};
private final ComponentDao componentDao;
private final System2 system2;
public PurgeDao(ComponentDao componentDao, System2 system2) {
this.componentDao = componentDao;
this.system2 = system2;
}
public void purge(DbSession session, PurgeConfiguration conf, PurgeListener listener, PurgeProfiler profiler) {
PurgeMapper mapper = session.getMapper(PurgeMapper.class);
PurgeCommands commands = new PurgeCommands(session, mapper, profiler);
String rootUuid = conf.rootProjectIdUuid().getUuid();
deleteAbortedAnalyses(rootUuid, commands);
deleteDataOfComponentsWithoutHistoricalData(session, rootUuid, conf.scopesWithoutHistoricalData(), commands);
purgeAnalyses(commands, rootUuid);
purgeDisabledComponents(session, conf, listener);
deleteOldClosedIssues(conf, mapper, listener);
}
private static void purgeAnalyses(PurgeCommands commands, String rootUuid) {
List<IdUuidPair> analysisUuids = commands.selectSnapshotIdUuids(
new PurgeSnapshotQuery()
.setComponentUuid(rootUuid)
.setIslast(false)
.setNotPurged(true));
commands.purgeAnalyses(analysisUuids);
}
private static void deleteOldClosedIssues(PurgeConfiguration conf, PurgeMapper mapper, PurgeListener listener) {
Date toDate = conf.maxLiveDateOfClosedIssues();
String rootUuid = conf.rootProjectIdUuid().getUuid();
List<String> issueKeys = mapper.selectOldClosedIssueKeys(rootUuid, dateToLong(toDate));
executeLargeInputs(issueKeys, input -> {
mapper.deleteIssueChangesFromIssueKeys(input);
return emptyList();
});
executeLargeInputs(issueKeys, input -> {
mapper.deleteIssuesFromKeys(input);
return emptyList();
});
listener.onIssuesRemoval(rootUuid, issueKeys);
}
private static void deleteAbortedAnalyses(String rootUuid, PurgeCommands commands) {
LOG.debug("<- Delete aborted builds");
PurgeSnapshotQuery query = new PurgeSnapshotQuery()
.setIslast(false)
.setStatus(UNPROCESSED_STATUS)
.setComponentUuid(rootUuid);
commands.deleteAnalyses(query);
}
private void deleteDataOfComponentsWithoutHistoricalData(DbSession dbSession, String rootUuid, String[] scopesWithoutHistoricalData, PurgeCommands purgeCommands) {
if (scopesWithoutHistoricalData.length == 0) {
return;
}
List<String> analysisUuids = purgeCommands.selectSnapshotUuids(
new PurgeSnapshotQuery()
.setComponentUuid(rootUuid)
.setIslast(false)
.setNotPurged(true));
List<String> componentWithoutHistoricalDataUuids = componentDao
.selectDescendants(
dbSession,
ComponentTreeQuery.builder()
.setBaseUuid(rootUuid)
.setQualifiers(Arrays.asList(scopesWithoutHistoricalData))
.setStrategy(Strategy.LEAVES)
.build())
.stream().map(ComponentDto::uuid)
.collect(MoreCollectors.toList());
purgeCommands.deleteComponentMeasures(analysisUuids, componentWithoutHistoricalDataUuids);
}
private void purgeDisabledComponents(DbSession session, PurgeConfiguration conf, PurgeListener listener) {
PurgeMapper mapper = mapper(session);
executeLargeInputs(conf.getDisabledComponentUuids(),
input -> {
mapper.deleteFileSourcesByUuid(input);
mapper.resolveComponentIssuesNotAlreadyResolved(input, system2.now());
return emptyList();
});
listener.onComponentsDisabling(conf.rootProjectIdUuid().getUuid(), conf.getDisabledComponentUuids());
session.commit();
}
public List<PurgeableAnalysisDto> selectPurgeableAnalyses(String componentUuid, DbSession session) {
List<PurgeableAnalysisDto> result = Lists.newArrayList();
result.addAll(mapper(session).selectPurgeableAnalysesWithEvents(componentUuid));
result.addAll(mapper(session).selectPurgeableAnalysesWithoutEvents(componentUuid));
// sort by date
Collections.sort(result);
return result;
}
public PurgeDao deleteProject(DbSession session, String uuid) {
PurgeProfiler profiler = new PurgeProfiler();
PurgeCommands purgeCommands = new PurgeCommands(session, profiler);
deleteProject(uuid, mapper(session), purgeCommands);
return this;
}
private static void deleteProject(String rootUuid, PurgeMapper mapper, PurgeCommands commands) {
List<IdUuidPair> childrenIds = mapper.selectComponentsByProjectUuid(rootUuid);
commands.deleteAnalyses(rootUuid);
commands.deleteComponents(childrenIds);
commands.deleteFileSources(rootUuid);
commands.deleteCeActivity(rootUuid);
commands.deleteCeQueue(rootUuid);
commands.deleteWebhookDeliveries(rootUuid);
}
public void deleteAnalyses(DbSession session, PurgeProfiler profiler, List<IdUuidPair> analysisIdUuids) {
new PurgeCommands(session, profiler).deleteAnalyses(analysisIdUuids);
}
private static PurgeMapper mapper(DbSession session) {
return session.getMapper(PurgeMapper.class);
}
}