package org.lucares.pdbui; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileTime; import java.time.Instant; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.concurrent.CustomizableThreadFactory; import org.springframework.stereotype.Component; @Component public class CleanupThread implements DisposableBean, PropertyKeys { private static final Logger LOGGER = LoggerFactory.getLogger(CleanupThread.class); private static final class RemoveTempFiles implements Runnable { private final Path outputPath; private final int cacheDurationInSeconds; public RemoveTempFiles(final Path outputPath, final int cacheDurationInSeconds) { this.outputPath = outputPath; this.cacheDurationInSeconds = cacheDurationInSeconds; } @Override public void run() { try { Files.walk(outputPath)// .filter(Files::isRegularFile)// .filter(this::isStale)// .forEach(RemoveTempFiles::delete); } catch (final IOException | RuntimeException e) { LOGGER.warn("failed to walk " + outputPath + ". Cannot delete stale files", e); } } private static void delete(final Path path) { try { LOGGER.debug("deleting stale file: " + path); Files.delete(path); } catch (final IOException e) { LOGGER.warn("failed to delete stale file " + path, e); } } private boolean isStale(final Path path) { final Instant maxAge = Instant.now().minusSeconds(cacheDurationInSeconds); try { final FileTime lastModifiedTime = Files.getLastModifiedTime(path); final Instant lastModifiedInstant = lastModifiedTime.toInstant(); return lastModifiedInstant.compareTo(maxAge) < 0; } catch (final IOException e) { LOGGER.warn("failed to get last modified time of " + path + ". Considering this file as stale.", e); return true; } } } private final ScheduledExecutorService scheduledThreadPool; @Autowired public CleanupThread(@Value("${" + PATH_GENERATED_IMAGES + "}") final String outputDir, @Value("${" + CACHE_IMAGES_DURATION_SECONDS + ":" + CACHE_IMAGES_DURATION_SECONDS_DEFAULT + "}") final int cacheDurationInSeconds) { scheduledThreadPool = Executors.newScheduledThreadPool(1, new CustomizableThreadFactory("cleanup-")); final Path outputPath = Paths.get(outputDir); scheduledThreadPool.scheduleWithFixedDelay(new RemoveTempFiles(outputPath, cacheDurationInSeconds), 1, 5, TimeUnit.MINUTES); } @Override public void destroy() { scheduledThreadPool.shutdown(); try { scheduledThreadPool.awaitTermination(10, TimeUnit.SECONDS); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } } }