DeletionManager.java
/*
* The coLAB project
* Copyright (C) 2021-2024 AlbaSim, MEI, HEIG-VD, HES-SO
*
* Licensed under the MIT License
*/
package ch.colabproject.colab.api.controller.common;
import ch.colabproject.colab.api.Helper;
import ch.colabproject.colab.api.controller.RequestManager;
import ch.colabproject.colab.api.controller.card.CardContentManager;
import ch.colabproject.colab.api.controller.card.CardManager;
import ch.colabproject.colab.api.controller.project.ProjectManager;
import ch.colabproject.colab.api.controller.security.SecurityManager;
import ch.colabproject.colab.api.model.ColabEntity;
import ch.colabproject.colab.api.model.common.DeletionStatus;
import ch.colabproject.colab.api.model.user.User;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.cp.lock.FencedLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ejb.LocalBean;
import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.ejb.TransactionAttribute;
import jakarta.ejb.TransactionAttributeType;
/**
* Handles the specific logic of the deletion process for any ColabEntity
* <p>
* Most coLAB entities are directly deleted in database.
* <p>
* But for
* <ul>
* <li>project</li>
* <li>card</li>
* <li>card content</li>
* </ul>
* there is a deletion workflow :
* <ol>
* <li>place in bin (manually)</li>
* <li>flag as to delete forever (manually)</li>
* <li>delete in database (launched by a scheduled cron job). This is what this class is about</li>
* </ol>
*
* @author sandra
*/
@Stateless
@LocalBean
public class DeletionManager {
/** logger */
private static final Logger logger = LoggerFactory.getLogger(DeletionManager.class);
// *********************************************************************************************
// injections
// *********************************************************************************************
/**
* Project specific logic handling
*/
@Inject
private ProjectManager projectManager;
/**
* Card specific logic handling
*/
@Inject
private CardManager cardManager;
/**
* Card content specific logic handling
*/
@Inject
private CardContentManager cardContentManager;
/**
* Access control manager
*/
@Inject
private SecurityManager securityManager;
/**
* request manager
*/
@Inject
private RequestManager requestManager;
/** hazelcast instance */
@Inject
private HazelcastInstance hzInstance;
// *********************************************************************************************
// check deletion status
// *********************************************************************************************
/**
* @param object The colab entity to check
* @return True if the colab entity is deleted, false otherwise
*/
public boolean isAlive(ColabEntity object) {
return object.getDeletionStatus() == null;
}
/**
* @param object The colab entity to check
* @return True if the colab entity is deleted, false otherwise
*/
public boolean isDeleted(ColabEntity object) {
return object.getDeletionStatus() != null;
}
// *********************************************************************************************
// put in bin
// *********************************************************************************************
/**
* Put the object in the bin.
* <br>
* Set the deletion status to BIN and initialise the erasure tracking data.
* <br>
* It means that the object is only visible in the bin panel.
*
* @param object Object to delete
*/
public void putInBin(ColabEntity object) {
logger.debug("put in bin {} # {} ", object.getClass(), object.getId());
User currentUser = securityManager.assertAndGetCurrentUser();
object.setDeletionStatus(DeletionStatus.BIN);
object.setErasureTrackingData(
currentUser != null ? currentUser.getUsername() : Helper.UNKNOWN_USER
);
}
// *********************************************************************************************
// restore from bin
// *********************************************************************************************
/**
* Restore the object from the bin. The object won't contain any deletion or erasure data anymore.
* <p>
* It means that the object is back at its place (as much as possible).
*
* @param object Object to delete
*/
public void restoreFromBin(ColabEntity object) {
logger.debug("restore from bin {} # {} ", object.getClass(), object.getId());
object.setDeletionStatus(null);
object.clearErasureTrackingData();
}
// *********************************************************************************************
// flag as to delete forever (no more visible from users)
// *********************************************************************************************
/**
* Set the deletion status to TO_DELETE.
* <p>
* It means that the object is not visible anymore .
*
* @param erasedByName Name of the "eraser" to fill the tracking data
* @param object Object to delete
*/
public void flagAsToDeleteForever(String erasedByName, ColabEntity object) {
logger.debug("flag as to delete forever {} # {} ", object.getClass(), object.getId());
object.setDeletionStatus(DeletionStatus.TO_DELETE);
object.setErasureTrackingData(erasedByName);
}
/**
* Set the deletion status to TO_DELETE.
* <p>
* It means that the object is not visible anymore .
*
* @param object Object to delete
*/
public void flagAsToDeleteForever(ColabEntity object) {
User currentUser = securityManager.assertAndGetCurrentUser();
flagAsToDeleteForever(
currentUser != null ? currentUser.getUsername() : Helper.UNKNOWN_USER, object);
}
/**
* Remove from bin all projects, cards, card contents that were there since a long time ago
* (the exact nb of days is set in configuration).
* Flag them as "ready-for-permanent-deletion"
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void cleanBinInTrn() {
logger.debug("DeletionManager.cleanBin");
FencedLock lock = hzInstance.getCPSubsystem().getLock("CleanBinScheduledJob");
if (lock.tryLock()) {
try {
requestManager.sudo(() -> {
try {
projectManager.removeOldProjectsFromBin();
} catch (Throwable anyThrowable) {
logger.error("ERROR when removing from bin old projects", anyThrowable);
}
try {
cardManager.removeOldCardsFromBin();
} catch (Throwable anyThrowable) {
logger.error("ERROR when removing from bin old cards", anyThrowable);
}
try {
cardContentManager.removeOldCardContentsFromBin();
} catch (Throwable anyThrowable) {
logger.error("ERROR when removing from bin old card contents", anyThrowable);
}
});
} finally {
lock.unlock();
}
}
}
// *********************************************************************************************
// delete forever (does not exist anymore in database)
// *********************************************************************************************
/**
* Delete all projects, cards, card contents that were flagged as "ready-for-permanent-deletion"
* a long time ago (the exact nb of days is set in configuration)
*/
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void deleteForeverInTrn() {
logger.debug("DeletionManager.deleteForever");
FencedLock lock = hzInstance.getCPSubsystem().getLock("DeleteForeverScheduledJob");
if (lock.tryLock()) {
try {
requestManager.sudo(() -> {
try {
projectManager.deleteForeverOldProjects();
} catch (Throwable anyThrowable) {
logger.error("ERROR when deleting forever old projects", anyThrowable);
}
try {
cardManager.deleteForeverOldCards();
} catch (Throwable anyThrowable) {
logger.error("ERROR when deleting forever old cards", anyThrowable);
}
try {
cardContentManager.deleteForeverOldCardContents();
} catch (Throwable anyThrowable) {
logger.error("ERROR when deleting forever old card contents", anyThrowable);
}
});
} finally {
lock.unlock();
}
}
}
}