How to create-then-atomically-rename file in Java on Windows?

I am trying to do "write temporary file and rename" using Java on Windows correctly.

How to atomically rename a file in Java, even if the dest file already exists? assumes that renaming the files is an “atomic operation” (whatever “atomic” it actually means), https://stackoverflow.com/a/168161/ offers to write a tmp file, and the renaming is cross-platform and ensures that the final the file either does not exist or can be processed by another process.

So, I tried to implement this approach. The following is a summary of my attempts. For the actual question - jump to the bottom.

recording methods

I tried various ways to write and rename the file ( content and charset - String and charset respectively):

Using java.nio.file.Files :

 Files.copy(new ByteArrayInputStream(content.getBytes(charset)), tmpFile); Files.move(tmpFile, finalFile, StandardCopyOption.ATOMIC_MOVE); 

Using Guava (14) and java.io.File :

 com.google.common.io.Files.write(content, tmpFile, charset); tmpFile.renameTo(finalFile); 

Or even more obscure approaches:

 try (OutputStream os = new FileOutputStream(tmpFile); Writer writer = new OutputStreamWriter(os, charset)) { writer.write(content); } Runtime.getRuntime().exec( new String[] { "cmd.exe", "/C", "move " + tmpFile + " " + finalFile }).waitFor(); 

reading methods

Now suppose that another thread (the thread, because I'm in the tests, in real life, it may be a different process) runs one of the following versions of code:

With common function:

 void waitUntilExists() throws InterruptedException { while (!java.nio.file.Files.exists(finalFile)) { NANOSECONDS.sleep(1); } } 

Using java.nio.file.Files :

 waitUntilExists(); return new String(Files.readAllBytes(finalFile), charset); 

Using Guava (14):

 waitUntilExists(); return new String(com.google.common.io.Files.toByteArray(finalFile.toFile()), charset); 

Or even more obscure approaches:

 waitUntilExists(); StringBuilder sb = new StringBuilder(); try (InputStream is = new FileInputStream(finalFile.toFile())) { byte[] buf = new byte[8192]; int n; while ((n = is.read(buf)) > 0) { sb.append(new String(buf, 0, n, charset)); } } return sb.toString(); 

results

If I read using the java.nio.file.Files approach, everything works fine.

If I run this code on Linux (outside the scope of this question, I know), everything works fine.

However, if I implement reading using Guava or FileInputStream , then with a probability above 0.5% (0.005) the test fails with

java.io.FileNotFoundException: the process cannot access the file because it is being used by another process

(The message translated by me because my windows are not English; the link to the “other process” is misleading, because for Windows this is normal, even if it is the same process, I checked with an explicit lock.)

Question

How to implement create-then-rename using Java on Windows so that the final file is displayed atomically, i.e. does not exist or cannot be read?

Since I have control over the processes than selecting files, I cannot accept any specific reading method, even if it is in Java. Therefore, the solution should work with all the reading methods listed above.

+6
source share
2 answers

This is similar to how Windows / NTFS behaves.

In addition, the behavioral difference between readings using older IOs and NIOs may be due to the fact that they use different Windows APIs.

Wikipedia about file locking says

For applications that use the Windows file read / write APIs, byte range locks (also called mandatory locks) on file systems that run on Windows. For applications that use the Windows File Association API, byte locks are not (also called advisory locks).

Although Wikipedia is not Windows docs, it still sheds some light.

(I put this answer only so that others who think alike should not write this. True answers with links to documents or error messages are much appreciated.)

0
source

There is an error report for the java.io.File.renameTo () function in the JDK, which is not atomic in Windows, that was closed using the Not fixed option: http://bugs.java.com/bugdatabase/view_bug. do? bug_id = 4017593 . Therefore, there is probably no clean method to fix your problem.

0
source

Source: https://habr.com/ru/post/986158/


All Articles