Question

Suppose I have I directory structure like the following:

MyDirectory
> File1
> File2
> File3
> UndeletableFile
> File4

Suppose also that I have already implemented a recursive algorithm for deleting the content of a folder and then the folder itself and/or used one of the excellent suggestions here

Now here's the catch to which I can't seem to find a solution after some searching on the web and on SO:
I want to delete the entire folder, but I want to do it only if it will succeed for every file and subfolder that is in there. The current implementation (of java.io.File.delete() to be precise) will delete a file immediately upon being called on it and then move on to the next file. If the deletion of a folder fails halfway through like in the example above there will be UndeletableFile and File5 as well as MyDirectory left on the system.

Is there a convenince method or a best practise algorithm for this case?
The goal is to let the deletable files live if the operation would fail halfway through. After all they shouldn't die if their home will survive, right?

What I have thought about so far:
Maybe it would be smart to just rename all files first instead of deleting them, like so

MyDirectory
> File1temp
> File2temp
> ...

After that if the process fails I could traverse the directory again and re-rename all files back to normal and if it succeeds I could delete them for good.

But this seams very inefficient to me, isn't there a better solution? It seems to me like this should be a common problem, please provide a link if I have overlooked something

Was it helpful?

Solution

There is no completely safe way to do this.

Your best bet will be to scan through all the files, check that you are allowed to delete them. Only if the scan succeeds do you then go through and do the actual deletion.

The catch is if something becomes locked after your scan but before you delete it then the deletion will still fail on that file.

The only alternative would be to keep a copy of all the files (i.e. move them to the recycle bin, store them in a zip, etc) until all the deletions have succeeded and only then empty the bin/delete the zip - if not restore.

But even then something could happen to block the restore.

Really you will have edge cases here, what you need to do is identify what they are and decide what the desired behaviour is in each case.

OTHER TIPS

You can use first a non-destructive method to check if any file can be deleted, e.g. on UNIX check that the file itself and its containing directory is writable. Then recursively calculate which directories can be deleted, and then in a second pass delete those files and directories that reside inside directories calculated to be deletable. The problem with this approach is that someone else may modify directory or file contents or permissions while the operation is going on, so you can still get wrong results.

Renaming is not a fool-proof solution either because if a program has a handle to a file or a directory, it can retain that handle while the object is renamed. So even if you rename a file or move it to a 'safe' location, someone else can change its permissions if there was an open handle to the file.

There isn't a fool-proof method available that would work on all platform under all conditions, because the underlying filesystem can be manipulated by other processes at the same time.

For a single user scenario, using the two-pass approach outlined in the first paragraph should work.

A common practice for your case, when you have to work with multiple files exclusively is to have a separate semaphore file and lock it before starting any operations on files bunch. A very primitive example would look like:

private static RandomAccessFile raf;

public static void main(String[] args) throws IOException {
    lock();
    //do smth...
    release();
}

private static boolean lock() throws IOException {
    File f = new File("Semaphore.lck");
    raf = new RandomAccessFile(f, "rw");
    FileLock fl = raf.getChannel().tryLock();
    return fl != null;
}

private static void release() throws IOException {
    raf.close();
}

A similar approach is taken in Apache Camel integration framework, have look at it's File component. The only problem is that second process should follow same protocol too, otherwise it would not have any effect.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top