I’m using Files.walkFileTree to delete a directory. When running this code in CentOS, I got an error in my server log.
if (Files.exists(p)) { if (Files.isDirectory(p)) { Files.walkFileTree(p, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { try { Files.delete(file); } catch (NoSuchFileException ignored) { } return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { if (e == null) { try { Files.delete(dir); } catch (NoSuchFileException ignored) { } return FileVisitResult.CONTINUE; } else { // directory iteration failed throw e; } } }); }
Caused by: java.nio.file.NoSuchFileException: /home/cluster/apache-tomcat-9.0.21/webapps/webroot/spider/db/copyT_92A501/super/P-1/S-1/day_of_year-col-6-dic at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86) ~[?:1.8.0_202] at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102) ~[?:1.8.0_202] at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107) ~[?:1.8.0_202] at sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55) ~[?:1.8.0_202] at sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:144) ~[?:1.8.0_202] at sun.nio.fs.LinuxFileSystemProvider.readAttributes(LinuxFileSystemProvider.java:99) ~[?:1.8.0_202] at java.nio.file.Files.readAttributes(Files.java:1737) ~[?:1.8.0_202] at java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:219) ~[?:1.8.0_202] at java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:276) ~[?:1.8.0_202] at java.nio.file.FileTreeWalker.next(FileTreeWalker.java:372) ~[?:1.8.0_202] at java.nio.file.Files.walkFileTree(Files.java:2706) ~[?:1.8.0_202] at java.nio.file.Files.walkFileTree(Files.java:2742) ~[?:1.8.0_202]
I searched some keywords on the internet and there is no answer yet, maybe something is wrong with my code. So I wonder is it because Files.walkFileTree is not thread safe or something? I think maybe there is another thread changing the file here (maybe). Or maybe because of some thread is reading the files inside at that moment?
Advertisement
Answer
The Files.walkFileTree
isn’t implementing a read-consistent view of the file system – that would be an unreasonable expectation. A scan might run for a significant length of time and it will be retrieving the latest contents of each particular directory traversal at any particular moment and not for the instant at which the scan began.
When it encounters an inconsistency in the items it has scanned – such as a file that was in some directory but deleted by some external thread or process – it correctly reports to the visitFileFailed
callback. This is expected, just like new FileReader(file)
would fail if another thread deletes the file moments before.
As you haven’t implemented visitFileFailed
you get this default behaviour of SimpleFileVisitor
– and therefore the stack trace you showed above:
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { throw exc; }
Your own code can easily deal with this situation, as no such file is fine if you want to delete it anyhow:
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { if (exc instanceof NoSuchFileException) { // This is OK, as no need to delete it return FileVisitResult.CONTINUE; } // Another common issue is access denied: else if (exc instanceof java.nio.file.AccessDeniedException) { // log and continue return FileVisitResult.CONTINUE; } // Something else, you could store the exception for processing at end, or just: throw exc; }
Similarly a file could be deleted by another process or thread while you are receiving the visitFile(Path file, BasicFileAttributes attrs)
callback so your own code needs to be defensive on the use of file
and attrs
, just as File.list()
could return details of deleted files.