diff --git a/src/main/java/org/codehaus/plexus/util/FileUtils.java b/src/main/java/org/codehaus/plexus/util/FileUtils.java index f14771c8..a09fa1a1 100644 --- a/src/main/java/org/codehaus/plexus/util/FileUtils.java +++ b/src/main/java/org/codehaus/plexus/util/FileUtils.java @@ -66,6 +66,7 @@ import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; @@ -1176,23 +1177,20 @@ public static void forceDelete(final File file) throws IOException { if (file.isDirectory()) { deleteDirectory(file); } else { - /* - * NOTE: Always try to delete the file even if it appears to be non-existent. This will ensure that a - * symlink whose target does not exist is deleted, too. - */ - boolean filePresent = file.getCanonicalFile().exists(); - if (!deleteFile(file) && filePresent) { - final String message = "File " + file + " unable to be deleted."; - throw new IOException(message); + if (!deleteFile(file)) { + throw new IOException("File " + file + " unable to be deleted."); } } } /** - * Accommodate Windows bug encountered in both Sun and IBM JDKs. Others possible. If the delete does not work, call + * Deletes a file, or a symlink without following it. A file that does not exist (including a dangling symlink, + * which {@link Files#deleteIfExists} removes) counts as deleted and returns immediately. Only if the file still + * exists after a failed attempt, accommodate the Windows bug encountered in both Sun and IBM JDKs: call * System.gc(), wait a little and try again. * * @param file a file + * @return {@code true} if the file no longer exists * @throws IOException if any */ private static boolean deleteFile(File file) throws IOException { @@ -1200,21 +1198,32 @@ private static boolean deleteFile(File file) throws IOException { throw new IOException("File " + file + " isn't a file."); } - if (!file.delete()) { - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - file = file.getCanonicalFile(); - System.gc(); + final Path path = file.toPath(); + try { + Files.deleteIfExists(path); + return true; + } catch (IOException e) { + if (!Files.exists(path, LinkOption.NOFOLLOW_LINKS)) { + return true; } + } - try { - Thread.sleep(10); - return file.delete(); - } catch (InterruptedException ignore) { - return file.delete(); - } + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + System.gc(); } - return true; + try { + Thread.sleep(10); + } catch (InterruptedException ignore) { + Thread.currentThread().interrupt(); + } + + try { + Files.deleteIfExists(path); + return true; + } catch (IOException e) { + return !Files.exists(path, LinkOption.NOFOLLOW_LINKS); + } } /** diff --git a/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java b/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java index b3ed474a..fcc45367 100644 --- a/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java +++ b/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java @@ -425,6 +425,28 @@ void forceDeleteAFile2() throws Exception { assertFalse(destination.exists(), "Check No Exist"); } + @Test + void forceDeleteNonExistingFile() throws Exception { + final File missing = new File(getTestDirectory(), "does-not-exist.txt"); + assertFalse(missing.exists()); + FileUtils.forceDelete(missing); + assertFalse(missing.exists()); + } + + @Test + void forceDeleteDanglingSymlink() throws Exception { + final File target = new File(getTestDirectory(), "dangling-target.txt"); + final File symlink = new File(getTestDirectory(), "dangling-link"); + createFile(target, 1); + createSymlink(symlink, target); + assumeTrue(Files.isSymbolicLink(symlink.toPath()), "the platform created a real symlink"); + assertTrue(target.delete()); + // Windows stops reporting a file link as a symlink once its target is gone + assumeTrue(Files.isSymbolicLink(symlink.toPath()), "the platform keeps a dangling symlink"); + FileUtils.forceDelete(symlink); + assertFalse(Files.isSymbolicLink(symlink.toPath()), "Dangling symlink should be removed"); + } + @Test void copyFile1ToDir() throws Exception { final File directory = new File(getTestDirectory(), "subdir");