public static void delete(final File rootFile) throws IOException {
        if (rootFile == null)
            return;
        Files.walkFileTree(rootFile.toPath(), new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException {
                if (exc instanceof NoSuchFileException) {
                    if (path.toFile().equals(rootFile)) {
                        // If the root path did not exist, ignore the error and terminate;
                        return FileVisitResult.TERMINATE;
                    } else {
                        // Otherwise, just continue walking as the file might already be deleted by other threads.
                        return FileVisitResult.CONTINUE;
                    }
                }
                throw exc;
            }

            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
                Files.deleteIfExists(path);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException {
                // KAFKA-8999: if there's an exception thrown previously already, we should throw it
                if (exc != null) {
                    throw exc;
                }

                Files.deleteIfExists(path);
                return FileVisitResult.CONTINUE;
            }
        });
    }
@SuppressWarnings("unchecked")
    @Test
    public void testRecursiveDeleteWithDeletedFile() throws IOException {
        // Test recursive deletes, where the FileWalk is supplied with a deleted file path.
        File rootDir = TestUtils.tempDirectory();
        File subDir = TestUtils.tempDirectory(rootDir.toPath(), "a");

        DirectoryStream<Path> mockDirectoryStream = (DirectoryStream<Path>) mock(DirectoryStream.class);
        FileSystemProvider mockFileSystemProvider = mock(FileSystemProvider.class);
        FileSystem mockFileSystem = mock(FileSystem.class);
        Path mockRootPath = mock(Path.class);
        BasicFileAttributes mockBasicFileAttributes = mock(BasicFileAttributes.class);
        Iterator<Path> mockIterator = mock(Iterator.class);
        File spyRootFile = spy(rootDir);

        when(spyRootFile.toPath()).thenReturn(mockRootPath);
        when(mockRootPath.getFileSystem()).thenReturn(mockFileSystem);
        when(mockFileSystem.provider()).thenReturn(mockFileSystemProvider);
        when(mockFileSystemProvider.readAttributes(any(), (Class<BasicFileAttributes>) any(), any())).thenReturn(mockBasicFileAttributes);
        when(mockBasicFileAttributes.isDirectory()).thenReturn(true);
        when(mockFileSystemProvider.newDirectoryStream(any(), any())).thenReturn(mockDirectoryStream);
        when(mockDirectoryStream.iterator()).thenReturn(mockIterator);
        // Here we pass the rootDir to the FileWalk which removes all Files recursively,
        // and then we pass the subDir path again which is already deleted by this point.
        when(mockIterator.next()).thenReturn(rootDir.toPath()).thenReturn(subDir.toPath());
        when(mockIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);

        assertDoesNotThrow(() -> {
            Utils.delete(spyRootFile);
        });
        assertFalse(Files.exists(rootDir.toPath()));
        assertFalse(Files.exists(subDir.toPath()));
    }