KmeliaBinsScheduledPurger.java
/*
* Copyright (C) 2000 - 2025 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* "https://www.silverpeas.org/legal/floss_exception.html"
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.silverpeas.components.kmelia.service;
import org.silverpeas.core.admin.component.model.WAComponent;
import org.silverpeas.core.admin.service.AdminException;
import org.silverpeas.core.admin.service.ComponentInstManager;
import org.silverpeas.core.annotation.Service;
import org.silverpeas.core.contribution.publication.model.PublicationDetail;
import org.silverpeas.core.contribution.publication.service.PublicationService;
import org.silverpeas.core.initialization.Initialization;
import org.silverpeas.core.node.model.NodeDetail;
import org.silverpeas.core.node.model.NodePK;
import org.silverpeas.core.node.service.NodeService;
import org.silverpeas.core.persistence.Transaction;
import org.silverpeas.core.scheduler.Job;
import org.silverpeas.core.scheduler.JobExecutionContext;
import org.silverpeas.core.scheduler.Scheduler;
import org.silverpeas.core.scheduler.SchedulerProvider;
import org.silverpeas.core.scheduler.trigger.JobTrigger;
import org.silverpeas.kernel.SilverpeasRuntimeException;
import org.silverpeas.kernel.bundle.ResourceLocator;
import org.silverpeas.kernel.bundle.SettingBundle;
import org.silverpeas.kernel.logging.SilverLogger;
import javax.inject.Inject;
import java.time.LocalDate;
import java.util.Date;
import java.util.stream.Stream;
import static org.silverpeas.core.util.DateUtil.toLocalDate;
/**
* A service scheduled in time to clean out periodically the bin in each Kmelia instances of the
* items (both topics and publications) that have been removed since a given number of days. The
* purge scheduling and the delay in days can be parameterized in the
* <code>org/silverpeas/kmelia/settings/kmeliaSettings.properties</code> configuration file.
*
* @author mmoquillon
*/
@Service
public class KmeliaBinsScheduledPurger implements Initialization {
private static final String COMPONENT_NAME = "kmelia";
private static final String SETTINGS_NAME = "org.silverpeas.kmelia.settings.kmeliaSettings";
private static final String JOB_NAME = "BinOlderItemsDeleter";
@Inject
private KmeliaDeleter deleter;
@Inject
private ComponentInstManager componentInstManager;
@Inject
private NodeService nodeService;
@Inject
private PublicationService publicationService;
@Override
public void init() throws Exception {
String cron = getSchedulingCron();
if (!cron.isEmpty()) {
final Scheduler scheduler = SchedulerProvider.getVolatileScheduler();
scheduler.unscheduleJob(JOB_NAME);
scheduler.scheduleJob(new BinOlderItemsDeleter(), JobTrigger.triggerAt(cron));
}
new BinOlderItemsDeleter().execute(JobExecutionContext.createWith(JOB_NAME, new Date()));
}
@Override
public void release() throws Exception {
Scheduler scheduler = SchedulerProvider.getVolatileScheduler();
if (scheduler.isJobScheduled(JOB_NAME)) {
scheduler.unscheduleJob(JOB_NAME);
}
}
private String getSchedulingCron() {
SettingBundle settings = ResourceLocator.getSettingBundle(SETTINGS_NAME);
return settings.getString("kmelia.autoDeletionCron", "");
}
private class BinOlderItemsDeleter extends Job {
BinOlderItemsDeleter() {
super(JOB_NAME);
}
@Override
public void execute(final JobExecutionContext context) {
int delay = getDeletionDelay();
if (delay > 0) {
SilverLogger.getLogger(this)
.info("Purge Kmelia bins from contributions removed more than " + delay + " days");
try {
// browse all the root items in each Kmelia bins and for each of them apply the purge algo
Transaction.performInOne(() -> {
final LocalDate now = LocalDate.now();
WAComponent.getByName(COMPONENT_NAME)
.ifPresent(component ->
getAllInstanceIds(component)
.map(id -> COMPONENT_NAME + id)
.forEach(instanceId -> {
NodePK bin = new NodePK(NodePK.BIN_NODE_ID, instanceId);
nodeService.getChildrenDetails(bin).stream()
.filter(node -> isOlder(node, now))
.forEach(topic ->
deleter.deleteTopic(topic.getNodePK()));
publicationService.getDetailsByFatherPK(bin).stream()
.filter(publication -> isOlder(publication, now))
.forEach(publication ->
deleter.deletePublication(publication.getPK()));
}));
return null;
});
} catch (SilverpeasRuntimeException e) {
SilverLogger.getLogger(this).error(e.getMessage(), e);
}
}
}
private int getDeletionDelay() {
SettingBundle settings = ResourceLocator.getSettingBundle(SETTINGS_NAME);
int delay = settings.getInteger("kmelia.autoDeletionDelay", 0);
return Math.max(delay, 0);
}
private boolean isOlder(PublicationDetail publication, LocalDate date) {
// old publication in bin before the adding of the removal properties. In such situation, it
// is automatically deleted
if (!publication.isRemoved()) {
return true;
}
return isOlder(publication.getRemovalDate(), date);
}
private boolean isOlder(NodeDetail topic, LocalDate date) {
// old topic in bin before the adding of the removal properties. In such situation, it is
// automatically deleted
if (!topic.isRemoved()) {
return true;
}
return isOlder(topic.getRemovalDate(), date);
}
private boolean isOlder(Date removalDate, LocalDate date) {
final LocalDate removeDayDateWithDelay = toLocalDate(removalDate)
.plusDays(getDeletionDelay());
return removeDayDateWithDelay.isBefore(date) ||
removeDayDateWithDelay.isEqual(date);
}
private Stream<String> getAllInstanceIds(WAComponent component) {
try {
return Stream.of(componentInstManager.getAllCompoIdsByComponentName(component.getName()));
} catch (AdminException e) {
throw new SilverpeasRuntimeException(e);
}
}
}
}