199 lines
6.7 KiB
Java
199 lines
6.7 KiB
Java
package eu.eudat.service.storage;
|
|
|
|
import eu.eudat.commons.fake.FakeRequestScope;
|
|
import eu.eudat.data.StorageFileEntity;
|
|
import eu.eudat.model.StorageFile;
|
|
import eu.eudat.query.StorageFileQuery;
|
|
import gr.cite.tools.data.query.Ordering;
|
|
import gr.cite.tools.data.query.QueryFactory;
|
|
import gr.cite.tools.logging.LoggerService;
|
|
import jakarta.persistence.EntityManager;
|
|
import jakarta.persistence.EntityManagerFactory;
|
|
import jakarta.persistence.EntityTransaction;
|
|
import jakarta.persistence.OptimisticLockException;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
|
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationListener;
|
|
import org.springframework.context.annotation.Scope;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.IOException;
|
|
import java.time.Instant;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
@Service
|
|
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
|
|
public class StorageFileCleanupTask implements Closeable, ApplicationListener<ApplicationReadyEvent> {
|
|
private class CandidateInfo
|
|
{
|
|
private UUID id;
|
|
private Instant createdAt;
|
|
|
|
public UUID getId() {
|
|
return id;
|
|
}
|
|
|
|
public void setId(UUID id) {
|
|
this.id = id;
|
|
}
|
|
|
|
public Instant getCreatedAt() {
|
|
return createdAt;
|
|
}
|
|
|
|
public void setCreatedAt(Instant createdAt) {
|
|
this.createdAt = createdAt;
|
|
}
|
|
}
|
|
|
|
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(StorageFileCleanupTask.class));
|
|
private final StorageFileCleanupProperties _config;
|
|
private final ApplicationContext applicationContext;
|
|
private ScheduledExecutorService scheduler;
|
|
|
|
public StorageFileCleanupTask(
|
|
StorageFileCleanupProperties config,
|
|
ApplicationContext applicationContext) {
|
|
this._config = config;
|
|
this.applicationContext = applicationContext;
|
|
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onApplicationEvent(ApplicationReadyEvent event) {
|
|
long intervalSeconds = this._config .getIntervalSeconds();
|
|
if (this._config .getEnable() && intervalSeconds > 0) {
|
|
logger.info("File clean up run in {} seconds", intervalSeconds);
|
|
|
|
scheduler = Executors.newScheduledThreadPool(1);
|
|
scheduler.scheduleAtFixedRate(this::process, 10, intervalSeconds, TimeUnit.SECONDS);
|
|
} else {
|
|
scheduler = null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
if (scheduler != null) this.scheduler.close();
|
|
}
|
|
|
|
protected void process() {
|
|
if (!this._config.getEnable()) return;
|
|
try {
|
|
Instant lastCandidateCreationTimestamp = null;
|
|
while (true) {
|
|
|
|
CandidateInfo candidate = this.candidate(lastCandidateCreationTimestamp);
|
|
if (candidate == null) break;
|
|
lastCandidateCreationTimestamp = candidate.getCreatedAt();
|
|
|
|
logger.debug("Clean up file: {}", candidate.getId());
|
|
|
|
boolean successfullyProcessed = this.processStorageFile(candidate.getId());
|
|
if (!successfullyProcessed) {
|
|
logger.error("Problem processing file cleanups. {}", candidate.getId());
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
logger.error("Problem processing file cleanups. Breaking for next interval", ex);
|
|
}
|
|
}
|
|
|
|
private boolean processStorageFile(UUID fileId) {
|
|
EntityTransaction transaction = null;
|
|
EntityManager entityManager = null;
|
|
boolean success = false;
|
|
try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) {
|
|
try {
|
|
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);
|
|
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
|
|
StorageFileService storageFileService = this.applicationContext.getBean(StorageFileService.class);
|
|
entityManager = entityManagerFactory.createEntityManager();
|
|
|
|
transaction = entityManager.getTransaction();
|
|
transaction.begin();
|
|
|
|
StorageFileEntity item = queryFactory.query(StorageFileQuery.class).ids(fileId).isPurged(false).first();
|
|
success = true;
|
|
|
|
if (item != null) {
|
|
storageFileService.purgeSafe(fileId);
|
|
}
|
|
|
|
transaction.commit();
|
|
} catch (OptimisticLockException ex) {
|
|
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working
|
|
logger.debug("Concurrency exception getting file. Skipping: {} ", ex.getMessage());
|
|
if (transaction != null) transaction.rollback();
|
|
success = false;
|
|
} catch (Exception ex) {
|
|
logger.error("Problem getting list of file. Skipping: {}", ex.getMessage(), ex);
|
|
if (transaction != null) transaction.rollback();
|
|
success = false;
|
|
} finally {
|
|
if (entityManager != null) entityManager.close();
|
|
}
|
|
} catch (Exception ex) {
|
|
logger.error("Problem getting list of file. Skipping: {}", ex.getMessage(), ex);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
private CandidateInfo candidate(Instant lastCandidateCreationTimestamp) {
|
|
EntityTransaction transaction = null;
|
|
EntityManager entityManager = null;
|
|
CandidateInfo candidate = null;
|
|
try (FakeRequestScope fakeRequestScope = new FakeRequestScope()) {
|
|
try {
|
|
QueryFactory queryFactory = this.applicationContext.getBean(QueryFactory.class);
|
|
EntityManagerFactory entityManagerFactory = this.applicationContext.getBean(EntityManagerFactory.class);
|
|
entityManager = entityManagerFactory.createEntityManager();
|
|
|
|
transaction = entityManager.getTransaction();
|
|
transaction.begin();
|
|
|
|
StorageFileQuery query = queryFactory.query(StorageFileQuery.class)
|
|
.canPurge(true)
|
|
.isPurged(false)
|
|
.createdAfter(lastCandidateCreationTimestamp);
|
|
query.setOrder(new Ordering().addAscending(StorageFile._createdAt));
|
|
StorageFileEntity item = query.first();
|
|
|
|
if (item != null) {
|
|
entityManager.flush();
|
|
|
|
candidate = new CandidateInfo();
|
|
candidate.setId(item.getId());
|
|
candidate.setCreatedAt(item.getCreatedAt());
|
|
}
|
|
|
|
transaction.commit();
|
|
} catch (OptimisticLockException ex) {
|
|
// we get this if/when someone else already modified the notifications. We want to essentially ignore this, and keep working
|
|
logger.debug("Concurrency exception getting file. Skipping: {} ", ex.getMessage());
|
|
if (transaction != null) transaction.rollback();
|
|
candidate = null;
|
|
} catch (Exception ex) {
|
|
logger.error("Problem getting list of file. Skipping: {}", ex.getMessage(), ex);
|
|
if (transaction != null) transaction.rollback();
|
|
candidate = null;
|
|
} finally {
|
|
if (entityManager != null) entityManager.close();
|
|
}
|
|
} catch (Exception ex) {
|
|
logger.error("Problem getting list of file. Skipping: {}", ex.getMessage(), ex);
|
|
}
|
|
|
|
return candidate;
|
|
}
|
|
|
|
}
|