use NIO when available for recursive file deletion

This commit is contained in:
Eugene Zhuravlev
2015-02-13 18:03:54 +01:00
parent 3b80735407
commit 7d86632067
2 changed files with 122 additions and 2 deletions
@@ -24,6 +24,10 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -66,6 +70,81 @@ public class FileUtilRt {
private static String ourCanonicalTempPathCache = null;
protected static final boolean NIO_FILE_API_AVAILABLE;
// todo: replace reflection with normal code after migration to JDK 1.8
private static Method ourFilesDeleteIfExistsMethod;
private static Method ourFilesWalkMethod;
private static Method ourFileToPathMethod;
private static Object ourDeletionVisitor;
static {
boolean initSuccess = false;
try {
final Class<?> pathClass = Class.forName("java.nio.file.Path");
final Class<?> visitorClass = Class.forName("java.nio.file.FileVisitor");
final Class<?> filesClass = Class.forName("java.nio.file.Files");
ourFileToPathMethod = Class.forName("java.io.File").getMethod("toPath");
ourFilesWalkMethod = filesClass.getMethod("walkFileTree", pathClass, visitorClass);
ourFilesDeleteIfExistsMethod = filesClass.getMethod("deleteIfExists", pathClass);
final Class<?> fileVisitResultClass = Class.forName("java.nio.file.FileVisitResult");
final Object Result_Continue = fileVisitResultClass.getDeclaredField("CONTINUE").get(null);
final Object Result_Terminate = fileVisitResultClass.getDeclaredField("TERMINATE").get(null);
ourDeletionVisitor = Proxy.newProxyInstance(FileUtilRt.class.getClassLoader(), new Class[]{visitorClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (args.length == 2) {
final Object second = args[1];
if (second instanceof Throwable) {
throw (Throwable)second;
}
final String methodName = method.getName();
if ("visitFile".equals(methodName) || "postVisitDirectory".equals(methodName)) {
if (!performDelete(args[0])) {
return Result_Terminate;
}
}
}
return Result_Continue;
}
private boolean performDelete(@NotNull final Object fileObject) {
Boolean result = doIOOperation(new RepeatableIOOperation<Boolean, RuntimeException>() {
public Boolean execute(boolean lastAttempt) {
try {
//Files.deleteIfExists(file);
ourFilesDeleteIfExistsMethod.invoke(null, fileObject);
return Boolean.TRUE;
}
catch (InvocationTargetException e) {
if (!(e.getCause() instanceof IOException)) {
return Boolean.FALSE;
}
}
catch (IllegalAccessException e) {
return Boolean.FALSE;
}
return lastAttempt? Boolean.FALSE : null;
}
});
return Boolean.TRUE.equals(result);
}
});
initSuccess = true;
LOG.info("Using NIO-based file deletion");
}
catch (Throwable ignored) {
LOG.info("Was not able to detect NIO API");
ourFileToPathMethod = null;
ourFilesWalkMethod = null;
ourFilesDeleteIfExistsMethod = null;
ourDeletionVisitor = null;
}
NIO_FILE_API_AVAILABLE = initSuccess;
}
@NotNull
public static String getExtension(@NotNull String fileName) {
int index = fileName.lastIndexOf('.');
@@ -538,10 +617,44 @@ public class FileUtilRt {
* @return true if the file did not exist or was successfully deleted
*/
public static boolean delete(@NotNull File file) {
if (NIO_FILE_API_AVAILABLE) {
return deleteRecursivelyNIO(file);
}
return deleteRecursively(file);
}
protected static boolean deleteRecursivelyNIO(File file) {
try {
/*
Files.walkFileTree(file.toPath(), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.deleteIfExists(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.deleteIfExists(dir);
return FileVisitResult.CONTINUE;
}
});
*/
final Object pathObject = ourFileToPathMethod.invoke(file);
ourFilesWalkMethod.invoke(null, pathObject, ourDeletionVisitor);
}
catch (Exception e) {
LOG.info(e);
return false;
}
return true;
}
private static boolean deleteRecursively(@NotNull File file) {
File[] files = file.listFiles();
if (files != null) {
for (File child : files) {
if (!delete(child)) return false;
if (!deleteRecursively(child)) return false;
}
}
@@ -421,6 +421,13 @@ public class FileUtil extends FileUtilRt {
}
public static boolean delete(@NotNull File file) {
if (NIO_FILE_API_AVAILABLE) {
return deleteRecursivelyNIO(file);
}
return deleteRecursively(file);
}
private static boolean deleteRecursively(@NotNull File file) {
FileAttributes attributes = FileSystemUtil.getAttributes(file);
if (attributes == null) return true;
@@ -428,7 +435,7 @@ public class FileUtil extends FileUtilRt {
File[] files = file.listFiles();
if (files != null) {
for (File child : files) {
if (!delete(child)) return false;
if (!deleteRecursively(child)) return false;
}
}
}