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}