JDK7 では、Windowsでもファイルのアトミックな上書きリネームができる

JDK7になって、以前はできなかった Windows上でのファイルのアトミックな上書きリネームができるようになっていてうれしい。

ソース

package example.jdk7;

import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

public class FilesMoveTest {
  public static void main(String[] args) throws Exception {
    FileSystem fs = FileSystems.getDefault();
    Path source = fs.getPath(args[0]);
    Path target = fs.getPath(args[1]);
    System.out.printf("from [%s] to [%s]\n", source, target);
    Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
  }
}

ATOMIC_MOVE 指定時には他のオプションは全て無視される、とドキュメントにあるので、REPLACE_EXISITING は指定していない。

ドキュメントには、ATOMIC_MOVE 指定時に移動先が上書きされるか移動に失敗するかは実装依存とあるけど、一応 Windows の場合は ATOMIC_MOVE 指定時には内部で MoveFileEx(移動元, 移動先, 1) が呼び出されていて、第三引数のフラグに MOVEFILE_REPLACE_EXISTING を示す 1 が指定されているので*1、移動先にファイルが存在すると上書きしてくれる(ロックされていなければ)。

確認

> java example.jdk7.FilesMoveTest C:\temp\test1.txt C:\temp\test2.txt

from [C:\temp\test1.txt] to [C:\temp\test2.txt]

C:\temp を覗くと、test2.txt のみが存在していて、正しく「上書きリネーム」できていることがわかる。

test2.txt がロックされている場合、

from [C:\temp\test1.txt] to [C:\temp\test2.txt]
Exception in thread "main" java.nio.file.AccessDeniedException: C:\temp\test1.txt -> C:\temp\test2.txt
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:83)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
	at sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:301)
	at sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:286)
	at java.nio.file.Files.move(Files.java:1339)
	at example.jdk7.FilesMoveTest.main(FilesMoveTest.java:15)

当然、移動に失敗。

別ドライブに移動しようとした場合、

> java example.jdk7.FilesMoveTest C:\temp\test1.txt D:\temp\test2.txt

from [C:\temp\test1.txt] to [D:\temp\test2.txt]
Exception in thread "main" java.nio.file.AtomicMoveNotSupportedException: C:\temp\test1.txt -> D:\temp\test2.txt: ファイルを別のディスク ドライブに移動できません。

	at sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:296)
	at sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:286)
	at java.nio.file.Files.move(Files.java:1339)
	at example.jdk7.FilesMoveTest.main(FilesMoveTest.java:21)

別ドライブの場合は「アトミックな移動」はできないので(コピー扱いのため)例外が発生する。