The symptoms are simple: Windows refuses to delete a directory because it thinks that its contents are still there -- although the recursive delete just deleted them. So my first guess is that there is a flush/sync or similar missing on the parent directory after deleting its contents and before deleting it.
I tried commons-io version 2.5 FileUtils.deleteDirectory and FileUtils.cleanDirectory functions as well as my own simplified test:
#Test
public void testMySimpleDelete() throws IOException, InterruptedException {
File dir = TEST_DIR;
for (int i = 0; i < 10; i++) {
dir = new File(dir, Integer.toString(i));
}
for (int i = 0; i < 1000; i++) {
LOG.info(""+i);
assertTrue("loop #" + i, dir.mkdirs());
mySimpleDelete(Paths.get(TEST_DIR.getAbsolutePath()));
assertFalse(TEST_DIR.exists());
}
}
private void mySimpleDelete(Path file) throws IOException, InterruptedException {
Files.walkFileTree(file, new FileVisitor<Path>() {
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
//LOG.info(dir.toString());
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
All show the same results/symptoms.
If you want to verify this behavior for yourself (on Windows only! Linux does not lock anything at all and behaves in a way more lenient way), simply check out https://github.com/jjYBdx4IL/java-evaluation and run "mvn test -Dtest=org.apache.commons.io.FileUtilsTest".
The problem was TortoiseGIT again. Last time it was so dumb and did not configure its packaged git.exe and used the one from my cygwin installation, which caused problems and wasted my time.
Now, in this case, the status cache, that is used to display status icons within Windows explorer, even scans git-ignored build and test directories, thereby locking stuff in there... omg. So bad.
Solution:
TortoiseGIT -> Settings -> Icon Overlays -> Status Cache set to None
Related
I am trying to move folder 114229494 from one path to another and I want to replace the existing folder at the destination path (D:\SampleP2) but I am getting DirectoryNotEmptyException with the code mentioned below.
Since I don't want to change the name of the folder I mentioned D:\SampleP2\114229494 this as destination path
There are some images inside the folder.
Please help me to figure out what is wrong with this code.
public class MoveFiles {
private String source_path= "D:\\sampleP1\\114229494";
private String destination_path= "D:\\SampleP2\\114229494";
public void movefolder() {
File source = new File(source_path);
File destination = new File(destination_path);
Path path1 = FileSystems.getDefault().getPath(source_path);
Path path2 = FileSystems.getDefault().getPath(destination_path);
System.out.println(source);
System.out.println(destination);
try {
Files.move(path1, path2, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
System.out.println("there is file in uploads");
}
}
}
If path1 is a directory and path2 exists already you cannot use Files.move(path1, path2) nor Files.move(path1, path2, StandardCopyOption.REPLACE_EXISTING) as these give rise to either FileAlreadyExistsException or DirectoryNotEmptyException.
A reliable move operation for occasions where the destination directory exists already needs to traverse the source directory tree and move each file to the same location under the destination, cleaning up folders as they are emptied.
The Files.walkFileTree method handles traversals, add a suitable FileVisitor action to move / merge the path1=>path2 files:
public static void move(final Path source, final Path dest) throws IOException {
FileVisitor<Path> visitor = new FileVisitor<>() {
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
final Path target = dest.resolve(source.relativize(dir));
System.out.println("Files.createDirectories("+target+")");
Files.createDirectories(target);
return FileVisitResult.CONTINUE;
}
public FileVisitResult visitFile(Path p, BasicFileAttributes attrs) throws IOException {
final Path targ = dest.resolve(source.relativize(p));
System.out.println("Files.move("+p+", "+targ+", StandardCopyOption.REPLACE_EXISTING)");
Files.move(p, targ, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
// Dir should be empty by now or there is coding error:
System.out.println("Files.deleteIfExists("+dir+")");
if (!Files.deleteIfExists(dir))
throw new IOException("Failed to delete src dir: "+dir);
return FileVisitResult.CONTINUE;
}
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
throw exc;
}
};
Files.walkFileTree(source, visitor);
}
Note that the above works for files or folders - Files.walkFileTree handles calling the appropriate callbacks.
The logic of the above move could be made significantly quicker if you use File.move(subdir,targsubdir) if detecting that a subdir is not found in the target - replacing createDirectories / deleteIfExists. That would avoid need to move every file underneath that tree. However I will leave that as an exercise for the reader.
Java nio directory delete gives java.nio.file.DirectoryNotEmptyException if the directory is open in windows explorer.
I am using Java 1.7 and below is the code for DeleteDirVisitor's visitFile and postVisitDirectory methods.
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
if(exc == null){
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
throw exc;
}
Strangely when i debug the delete call then it doesn't throw any exception but when i run then it fails.
Seems to be a timing issue.
Also, the above API's work 100% if i close the explorer window pointing to the directory being deleted.
Can someone suggest me a work around so that i can get it working 100%.
Thanks,
I wrote a program for deleting a folder with all of its sub folders and files using Java NIO 2 features. The program run smoothly in case NO Window explorer is opened. But it will raises an error when Window explorer is opened(i.e. The deleting folder is "E:\Error" and i stayed at "E:"). Here is the structure of testing folder:
"E:\Error\Error0\Error1\Error2\Error3\Error4\Error5\Error6\Error7\Error8\Error9"
There are two file : "Text.txt" and "Text2.txt" in the "Error2" folder.
This is my class:
public class FileVisitorForTest extends SimpleFileVisitor<Path> {
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
System.out.println(dir);
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
public static void main(String[] args) throws IOException {
Path targetFolder = Paths.get("E:\\Error");
Files.walkFileTree(targetFolder, new FileVisitorForTest());
}
}
The error show in console when i run program with window file explorer is opening:
Exception in thread "main" java.nio.file.DirectoryNotEmptyException: E:\Error\Error0\Error1
at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:265)
at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
at java.nio.file.Files.delete(Files.java:1077)
at nio.FileVisitorForTest.postVisitDirectory(FileVisitorForTest.java:13)
at nio.FileVisitorForTest.postVisitDirectory(FileVisitorForTest.java:10)
at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:224)
at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:199)
at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:199)
at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:69)
at java.nio.file.Files.walkFileTree(Files.java:2600)
at java.nio.file.Files.walkFileTree(Files.java:2633)
at nio.FileVisitorForTest.main(FileVisitorForTest.java:25)
Please help me to understand this circumstance. Thank you all!
Ps: My OS is Windows 7 64 bit.
Update: This bug can be overcome by adding a Thread in the postVisitDirectory() method like this:
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
But i still don't know why.
A folder can not be deleted when it is still open in an explorer window. Its a Windows thing, not a Java thing.
You can check if the file is locked by another process.
File file = new File("some_file.txt");
System.out.println(file.canWrite());
The following is some simple code just to test the Files.walkFileTree() method. However, the folder /etc/ssl/private, which has these permissions (rwx--x---), throws an exception, even when I thought I guarded it with an if statement (if (permissions.equals("rwx--x---")).
What am I doing wrong? Thanks in advance.
public static void main (String []args) throws IOException, InterruptedException
{
Files.walkFileTree(Paths.get("/"), new WalkingTheThing2());
}
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
PosixFileAttributeView posixView = Files.getFileAttributeView(dir, PosixFileAttributeView.class);
PosixFileAttributes posixAttr = posixView.readAttributes();
String permissions =PosixFilePermissions.toString(posixAttr.permissions());
if (permissions.equals("rwx--x---"))
{
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
try{
System.out.println(file.getFileName()+" " +Files.size(file));
return FileVisitResult.CONTINUE;
}
catch(IOException io){return FileVisitResult.CONTINUE;}
}
The exception I get is: java.nio.file.AccessDeniedException: /etc/ssl/private
EDIT: Solved by overriding visitFileFailed:
public FileVisitResult visitFileFailed(Path file, IOException io)
{
return FileVisitResult.SKIP_SUBTREE;
}
If you are traversing the whole directory System and there is a situation where you got some type of Exception like AccessDeniedException and you want skip that file so that you could check the other file you need to override the visitFileFailed and skip that file or directory.
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
System.err.printf("Visiting failed for %s\n", file);
return FileVisitResult.SKIP_SUBTREE;
}
This is the work around I have found when Walking through the directory system.
Although overriding visitFileFailed solved your problem, it might be hiding the fact you are still doing several things wrong:
Files.getFileAttributeView can return null (e.g. if the file system does not support POSIX file permissions) making posixView.readAttributes() fail on NPE
posixView.readAttributes() can itself throw exceptions (e.g. if you don't have the required permissions to read the file's permissions) - this might be the cause of the AccessDeniedException you got
Not entirely wrong, but comparing the string representation of the permissions might be inappropriate for this use case, unless you want to explicitly check that the file has the given permissions - and does not have the the others; a different approach would be to check just for the required permissions:
Set<PosixFilePermission> perm = posixAttr.permissions();
if (perm.contains(OWNER_READ) || perm.contains(GROUP_READ) || perm.contains(OTHERS_READ)) {
return FileVisitResult.SKIP_SUBTREE;
}
I am trying to walk the file tree and delet all files/directories. The code is below:
Files.walkFileTree(metricPath, new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir,
IOException exc) throws IOException {
if (exc == null) {
Files.delete(dir);
return FileVisitResult.CONTINUE;
} else {
throw exc;
}
}
});
}
This code is run in between unit tests, each of which is generating a separate file, in the form folder1/folder2/file. When I try to walk that tree, The DirectoryNotEmptyException is thrown when folder1 attempts to be deleted, although it is clearly empty...
Have you checked that dir for hidden files? On Windows it could be that some process have opened this directory and opened file HANDLE still exists in system HANDLE table. In that case directory is locked and java could throw that exception.
As I can see it on your code, there should not be a problem, not unless one file/folder is in read only mode. you may want to explore changing the file permission first before deleting.
can you also try to put Files.delete() method on the following override
public FileVisitResult visitFileFailed(Path file, IOException exc)
Reference:
JAVA NIO Directory Delete
Use Apache Commons FileUtils.deleteDirectory() or FileUtils.deleteQuietly()
Alternatively, you can import Apache Commons IO and use its FileUtils.deleteDirectory(File directory) method. Just one line is enough as it deletes all files and subdirectories recursively:
FileUtils.deleteDirectory(dirToBeDeleted);