001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.core.util;
007
008import java.io.IOException;
009import java.nio.file.FileVisitResult;
010import java.nio.file.Files;
011import java.nio.file.Path;
012import java.nio.file.SimpleFileVisitor;
013import java.nio.file.attribute.BasicFileAttributes;
014import java.util.LinkedHashSet;
015import java.util.Set;
016import java.util.concurrent.locks.Lock;
017import java.util.concurrent.locks.ReentrantLock;
018
019/**
020 * Used to perform cleanup on shutdown.
021 */
022@SuppressWarnings("PMD.DoNotUseThreads")
023public final class DeleteOnShutdown {
024  private static Set<Path> paths = new LinkedHashSet<>();
025  private static final Lock LOCK = new ReentrantLock();
026
027  static {
028    Runtime.getRuntime().addShutdownHook(
029        new Thread(DeleteOnShutdown::shutdownHook));
030  }
031
032  @SuppressWarnings("PMD.NullAssignment")
033  private static void shutdownHook() {
034    LOCK.lock();
035    try {
036      Set<Path> localSet = new LinkedHashSet<>(paths);
037      paths = null;
038      localSet.forEach(path -> {
039        try {
040          Files.walkFileTree(path,
041              new SimpleFileVisitor<>() {
042                @Override
043                public FileVisitResult postVisitDirectory(
044                    Path dir, IOException exc) throws IOException {
045                  Files.delete(dir);
046                  return FileVisitResult.CONTINUE;
047                }
048
049                @Override
050                public FileVisitResult visitFile(
051                    Path file, BasicFileAttributes attrs)
052                    throws IOException {
053                  Files.delete(file);
054                  return FileVisitResult.CONTINUE;
055                }
056              });
057        } catch (@SuppressWarnings("unused") IOException ex) {
058          // this is a best effort, ignore the error
059        }
060      });
061    } finally {
062      LOCK.unlock();
063    }
064  }
065
066  /**
067   * Register a new path to be deleted on JVM termination.
068   * <p>
069   * If the path is a directory, then its contents will also be deleted.
070   *
071   * @param path
072   *          the path to delete
073   */
074  public static void register(Path path) {
075    LOCK.lock();
076    try {
077      if (paths == null) {
078        throw new IllegalStateException("ShutdownHook already in progress.");
079      }
080      paths.add(path);
081    } finally {
082      LOCK.unlock();
083    }
084  }
085
086  private DeleteOnShutdown() {
087    // disable construction
088  }
089
090}