5

I am trying to understand the following behavior. My older code,

String path = "C:/temp/sample.txt";
String mode= "rw";
FileChannel channel = new RandomAccessFile(path, mode).getChannel();
// some code to write to this file

// finally delete
File file = new File(path);
boolean isDeleted = file.delete();
System.out.println("Is Deleted - " + isDeleted);

O/P - Is Deleted - false

Only if i do a "channel.close();" before i delete the file. Does it delete the file and return true.

Newer replaced code,

String path = "C:/temp/sample.txt";
FileChannel fChannel = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
// some code to write to this file

// finally delete
File file = new File(path);
boolean isDeleted = file.delete();
System.out.println("Is Deleted - " + isDeleted);

O/P - Is Deleted - true

But this does not delete the file until the application exits. If i use "fChannel.close()", it will delete it immediately.

Few questions,

  1. Why different behaviors, i understand both are different types, i.e. RA vs Seekable Channel. But not sure, why delete should behave differently.
  2. In the newer implementation, if it does not delete the file until, the application exits, then it should either return false (i.e. will not delete, until close is called) or then delete immediately.

I don't know if i have hit a bug or missing something. Any pointers can help.

Thanks

Victor
  • 1,207
  • 2
  • 13
  • 21

1 Answers1

1

From the specification of RandomAccessFile.getChannel():

The position of the returned channel will always be equal to this object's file-pointer offset as returned by the getFilePointer method. Changing this object's file-pointer offset, whether explicitly or by reading or writing bytes, will change the position of the channel, and vice versa. Changing the file's length via this object will change the length seen via the file channel, and vice versa.

In other words, the returned channel and the RandomAccessFile maintain a bidirectional relationship and both are either open or closed. So in this regard, it’s not the FileChannel but the RandomAccessFile, which is still open while the channel is open, that keeps the File locked.

When you call close on such a FileChannel it will close the associated RandomAccessFile as well, letting nothing remain within the JRE preventing the delete operation.

In contrast, when creating a FileChannel via FileChannel.open it doesn’t have an associated FileInputStream, FileOutputStream, nor RandomAccessFile and doesn’t prevent a File.delete operation.

So when there is nothing within the JVM/JRE preventing the delete operation, it will exhibit the behavior of the underlying operating system, e.g. on Microsoft Windows:

DeleteFile function

Deletes an existing file.

The DeleteFile function marks a file for deletion on close. Therefore, the file deletion does not occur until the last handle to the file is closed. Subsequent calls to CreateFile to open the file fail with ERROR_ACCESS_DENIED.

This is exactly the observed behavior. You don’t need to exit the JVM, closing the FileChannel is enough to complete the deletion.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Thanks this surely explain part 1 (i.e. why). About #2(i.e. how), i know, closing the channel, will effect immediate delete. But say i want to make the delete behavior like my older code, i.e. file.delete() should return false, because the channel is still open (not yet closed). Is there a way to do this? I tried locking, but that works well for "rw" mode, it can't be used for "r" mode. Any ideas. – Victor Dec 04 '14 at 06:18
  • How does locking help? As far as I can see, locking doesn’t change the behavior of `delete`, regardless of whether shared or exclusive. It’s only the presence of a `RandomAccessFile` that makes a difference, but its behavior is buried down the native code, so I don’t see any way for the Java side (besides keeping a `RandomAccessFile` file open…) – Holger Dec 04 '14 at 11:04
  • Ok, the way i used locking was, every time the file was opened, i got an exclusive lock. Now during delete, rather than just blindly trying to delete, i tried taking a "rw" lock again before deleting, this failed with OverlappingLockException. Which is indicative of someone else still holding the handle to it. And so not to allow delete in that case. Mimicking RAF delete behavior. – Victor Dec 05 '14 at 05:02
  • That would work as well if you acquire a read lock every time you open the file for reading and try to acquire a write lock when you want to delete it. There is the small corner case that you could have delete permission without write permission on a file. Then locking a region for write will always fail even when deleting is possible. – Holger Dec 05 '14 at 13:45
  • True, there are some other corner cases as well, which might break the above logic. So finally, i don't see a clear way where i can mimic RAF exactly for all cases using a FileChannel.open. Anyway thanks for all your help. – Victor Dec 06 '14 at 15:10