Use NIO2 to implement FileUtils.rename() and expose options
- use NIO2's Files.move() to reimplement rename() - provide a second method accepting CopyOptions which can be used to request atomic move. Change-Id: Ibcf722978e65745218a1ccda45344ca295911659 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
This commit is contained in:
parent
2e5c7c5db4
commit
548ba66a37
|
@ -48,6 +48,8 @@
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
@ -83,4 +85,14 @@ public void testDeleteSymlinkToDirectoryDoesNotDeleteTarget()
|
|||
assertTrue(dir.exists());
|
||||
assertTrue(file.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAtomicMove() throws IOException {
|
||||
File src = new File(trash, "src");
|
||||
Files.createFile(src.toPath());
|
||||
File dst = new File(trash, "dst");
|
||||
FileUtils.rename(src, dst, StandardCopyOption.ATOMIC_MOVE);
|
||||
assertFalse(Files.exists(src.toPath()));
|
||||
assertTrue(Files.exists(dst.toPath()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,12 @@
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.Normalizer;
|
||||
import java.text.Normalizer.Form;
|
||||
|
@ -212,30 +215,68 @@ public static void delete(final File f, int options) throws IOException {
|
|||
*/
|
||||
public static void rename(final File src, final File dst)
|
||||
throws IOException {
|
||||
rename(src, dst, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a file or folder using the passed {@link CopyOption}s. If the
|
||||
* rename fails and if we are running on a filesystem where it makes sense
|
||||
* to repeat a failing rename then repeat the rename operation up to 9 times
|
||||
* with 100ms sleep time between two calls. Furthermore if the destination
|
||||
* exists and is a directory hierarchy with only directories in it, the
|
||||
* whole directory hierarchy will be deleted. If the target represents a
|
||||
* non-empty directory structure, empty subdirectories within that structure
|
||||
* may or may not be deleted even if the method fails. Furthermore if the
|
||||
* destination exists and is a file then the file will be replaced if
|
||||
* {@link StandardCopyOption#REPLACE_EXISTING} has been set. If
|
||||
* {@link StandardCopyOption#ATOMIC_MOVE} has been set the rename will be
|
||||
* done atomically or fail with an {@link AtomicMoveNotSupportedException}
|
||||
*
|
||||
* @param src
|
||||
* the old file
|
||||
* @param dst
|
||||
* the new file
|
||||
* @param options
|
||||
* options to pass to
|
||||
* {@link Files#move(java.nio.file.Path, java.nio.file.Path, CopyOption...)}
|
||||
* @throws AtomicMoveNotSupportedException
|
||||
* if file cannot be moved as an atomic file system operation
|
||||
* @throws IOException
|
||||
* @since 4.1
|
||||
*/
|
||||
public static void rename(final File src, final File dst,
|
||||
CopyOption... options)
|
||||
throws AtomicMoveNotSupportedException, IOException {
|
||||
int attempts = FS.DETECTED.retryFailedLockFileCommit() ? 10 : 1;
|
||||
while (--attempts >= 0) {
|
||||
if (src.renameTo(dst))
|
||||
return;
|
||||
try {
|
||||
if (!dst.delete())
|
||||
delete(dst, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
|
||||
// On *nix there is no try, you do or do not
|
||||
if (src.renameTo(dst))
|
||||
return;
|
||||
Files.move(src.toPath(), dst.toPath(), options);
|
||||
return;
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
// ignore and continue retry
|
||||
try {
|
||||
if (!dst.delete()) {
|
||||
delete(dst, EMPTY_DIRECTORIES_ONLY | RECURSIVE);
|
||||
}
|
||||
// On *nix there is no try, you do or do not
|
||||
Files.move(src.toPath(), dst.toPath(), options);
|
||||
return;
|
||||
} catch (IOException e2) {
|
||||
// ignore and continue retry
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(MessageFormat.format(
|
||||
JGitText.get().renameFileFailed, src.getAbsolutePath(),
|
||||
dst.getAbsolutePath()));
|
||||
throw new IOException(
|
||||
MessageFormat.format(JGitText.get().renameFileFailed,
|
||||
src.getAbsolutePath(), dst.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
throw new IOException(MessageFormat.format(
|
||||
JGitText.get().renameFileFailed, src.getAbsolutePath(),
|
||||
dst.getAbsolutePath()));
|
||||
throw new IOException(
|
||||
MessageFormat.format(JGitText.get().renameFileFailed,
|
||||
src.getAbsolutePath(), dst.getAbsolutePath()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue