77import java .nio .file .FileVisitResult ;
88import java .nio .file .FileVisitor ;
99import java .nio .file .Files ;
10+ import java .nio .file .NoSuchFileException ;
1011import java .nio .file .Path ;
1112import java .nio .file .Paths ;
1213import java .nio .file .attribute .BasicFileAttributes ;
@@ -31,6 +32,33 @@ private static final class SingletonHolder {
3132 private static final TempLocationManager INSTANCE = new TempLocationManager ();
3233 }
3334
35+ interface CleanupHook extends FileVisitor <Path > {
36+ CleanupHook EMPTY = new CleanupHook () {};
37+
38+ @ Override
39+ default FileVisitResult preVisitDirectory (Path dir , BasicFileAttributes attrs )
40+ throws IOException {
41+ return null ;
42+ }
43+
44+ @ Override
45+ default FileVisitResult visitFile (Path file , BasicFileAttributes attrs ) throws IOException {
46+ return null ;
47+ }
48+
49+ @ Override
50+ default FileVisitResult visitFileFailed (Path file , IOException exc ) throws IOException {
51+ return null ;
52+ }
53+
54+ @ Override
55+ default FileVisitResult postVisitDirectory (Path dir , IOException exc ) throws IOException {
56+ return null ;
57+ }
58+
59+ default void onCleanupStart () {}
60+ }
61+
3462 private class CleanupVisitor implements FileVisitor <Path > {
3563 private boolean shouldClean ;
3664
@@ -47,6 +75,8 @@ private class CleanupVisitor implements FileVisitor<Path> {
4775 @ Override
4876 public FileVisitResult preVisitDirectory (Path dir , BasicFileAttributes attrs )
4977 throws IOException {
78+ cleanupTestHook .preVisitDirectory (dir , attrs );
79+
5080 if (dir .equals (baseTempDir )) {
5181 return FileVisitResult .CONTINUE ;
5282 }
@@ -63,25 +93,40 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
6393
6494 @ Override
6595 public FileVisitResult visitFile (Path file , BasicFileAttributes attrs ) throws IOException {
66- if (Files .getLastModifiedTime (file ).toInstant ().isAfter (cutoff )) {
67- return FileVisitResult .SKIP_SUBTREE ;
96+ cleanupTestHook .visitFile (file , attrs );
97+ try {
98+ if (Files .getLastModifiedTime (file ).toInstant ().isAfter (cutoff )) {
99+ return FileVisitResult .SKIP_SUBTREE ;
100+ }
101+ Files .delete (file );
102+ } catch (NoSuchFileException ignored ) {
103+ // another process has already cleaned it; ignore
68104 }
69- Files .delete (file );
70105 return FileVisitResult .CONTINUE ;
71106 }
72107
73108 @ Override
74109 public FileVisitResult visitFileFailed (Path file , IOException exc ) throws IOException {
75- if (log .isDebugEnabled ()) {
110+ cleanupTestHook .visitFileFailed (file , exc );
111+ // do not log files/directories removed by another process running concurrently
112+ if (!(exc instanceof NoSuchFileException ) && log .isDebugEnabled ()) {
76113 log .debug ("Failed to delete file {}" , file , exc );
77114 }
78115 return FileVisitResult .CONTINUE ;
79116 }
80117
81118 @ Override
82119 public FileVisitResult postVisitDirectory (Path dir , IOException exc ) throws IOException {
120+ cleanupTestHook .postVisitDirectory (dir , exc );
121+ if (exc instanceof NoSuchFileException ) {
122+ return FileVisitResult .CONTINUE ;
123+ }
83124 if (shouldClean ) {
84- Files .delete (dir );
125+ try {
126+ Files .delete (dir );
127+ } catch (NoSuchFileException ignored ) {
128+ // another process has already cleaned it; ignore
129+ }
85130 String fileName = dir .getFileName ().toString ();
86131 // reset the flag only if we are done cleaning the top-level directory
87132 shouldClean = !fileName .startsWith ("pid_" );
@@ -96,6 +141,8 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx
96141
97142 private final CompletableFuture <Void > cleanupTask ;
98143
144+ private final CleanupHook cleanupTestHook ;
145+
99146 /**
100147 * Get the singleton instance of the TempLocationManager. It will run the cleanup task in the
101148 * background.
@@ -125,6 +172,12 @@ private TempLocationManager() {
125172 }
126173
127174 TempLocationManager (ConfigProvider configProvider ) {
175+ this (configProvider , CleanupHook .EMPTY );
176+ }
177+
178+ TempLocationManager (ConfigProvider configProvider , CleanupHook testHook ) {
179+ cleanupTestHook = testHook ;
180+
128181 // In order to avoid racy attempts to clean up files which are currently being processed in a
129182 // JVM which is being shut down (the JVMs far in the shutdown routine may not be reported by
130183 // 'jps' but still can be eg. processing JFR chunks) we will not clean up any files not older
@@ -222,6 +275,7 @@ public Path getTempDir(Path subPath, boolean create) {
222275
223276 void cleanup (boolean cleanSelf ) {
224277 try {
278+ cleanupTestHook .onCleanupStart ();
225279 Files .walkFileTree (baseTempDir , new CleanupVisitor (cleanSelf ));
226280 } catch (IOException e ) {
227281 if (log .isDebugEnabled ()) {
@@ -232,7 +286,8 @@ void cleanup(boolean cleanSelf) {
232286 }
233287 }
234288
235- private void waitForCleanup () {
289+ // accessible for tests
290+ void waitForCleanup () {
236291 cleanupTask .join ();
237292 }
238293}
0 commit comments