提交 25d907e3 authored 作者: Thomas Mueller's avatar Thomas Mueller

File system abstraction: support replacing existing files using move (currently not for Windows).

上级 d99f5601
......@@ -124,8 +124,10 @@ public abstract class FilePath {
* Rename a file if this is allowed.
*
* @param newName the new fully qualified file name
* @param atomicReplace whether the move should be atomic, and the target file
* should be replaced if it exists and replacing is possible
*/
public abstract void moveTo(FilePath newName);
public abstract void moveTo(FilePath newName, boolean atomicReplace);
/**
* Create a new file.
......
......@@ -79,7 +79,7 @@ public class FilePathDisk extends FilePath {
}
@Override
public void moveTo(FilePath newName) {
public void moveTo(FilePath newName, boolean atomicReplace) {
File oldFile = new File(name);
File newFile = new File(newName.name);
if (oldFile.getAbsolutePath().equals(newFile.getAbsolutePath())) {
......@@ -90,6 +90,16 @@ public class FilePathDisk extends FilePath {
name + " (not found)",
newName.name);
}
// Java 7: use java.nio.file.Files.move(Path source, Path target, CopyOption... options)
// with CopyOptions "REPLACE_EXISTING" and "ATOMIC_MOVE".
if (atomicReplace) {
boolean ok = oldFile.renameTo(newFile);
if (ok) {
return;
}
throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2,
new String[]{name, newName.name});
}
if (newFile.exists()) {
throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2,
new String[] { name, newName + " (exists)" });
......
......@@ -47,8 +47,13 @@ public class FilePathMem extends FilePath {
}
@Override
public void moveTo(FilePath newName) {
public void moveTo(FilePath newName, boolean atomicReplace) {
synchronized (MEMORY_FILES) {
if (!atomicReplace && !newName.name.equals(name) &&
MEMORY_FILES.containsKey(newName.name)) {
throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2,
new String[] { name, newName + " (exists)" });
}
FileMemData f = getMemoryFile();
f.setName(newName.name);
MEMORY_FILES.remove(name);
......
......@@ -46,8 +46,13 @@ public class FilePathNioMem extends FilePath {
}
@Override
public void moveTo(FilePath newName) {
public void moveTo(FilePath newName, boolean atomicReplace) {
synchronized (MEMORY_FILES) {
if (!atomicReplace && !name.equals(newName.name) &&
MEMORY_FILES.containsKey(newName.name)) {
throw DbException.get(ErrorCode.FILE_RENAME_FAILED_2,
new String[] { name, newName + " (exists)" });
}
FileNioMemData f = getMemoryFile();
f.setName(newName.name);
MEMORY_FILES.remove(name);
......
......@@ -70,9 +70,9 @@ public class FilePathRec extends FilePathWrapper {
}
@Override
public void moveTo(FilePath newPath) {
public void moveTo(FilePath newPath, boolean atomicReplace) {
log(Recorder.RENAME, unwrap(name) + ":" + unwrap(newPath.name));
super.moveTo(newPath);
super.moveTo(newPath, atomicReplace);
}
public boolean isTrace() {
......
......@@ -183,12 +183,12 @@ public class FilePathSplit extends FilePathWrapper {
}
@Override
public void moveTo(FilePath path) {
public void moveTo(FilePath path, boolean atomicReplace) {
FilePathSplit newName = (FilePathSplit) path;
for (int i = 0;; i++) {
FilePath o = getBase(i);
if (o.exists()) {
o.moveTo(newName.getBase(i));
o.moveTo(newName.getBase(i), atomicReplace);
} else {
break;
}
......
......@@ -128,8 +128,8 @@ public abstract class FilePathWrapper extends FilePath {
}
@Override
public void moveTo(FilePath newName) {
base.moveTo(((FilePathWrapper) newName).base);
public void moveTo(FilePath newName, boolean atomicReplace) {
base.moveTo(((FilePathWrapper) newName).base, atomicReplace);
}
@Override
......
......@@ -206,7 +206,7 @@ public class FilePathZip extends FilePath {
}
@Override
public void moveTo(FilePath newName) {
public void moveTo(FilePath newName, boolean atomicReplace) {
throw DbException.getUnsupportedException("write");
}
......
......@@ -102,14 +102,26 @@ public class FileUtils {
}
/**
* Rename a file if this is allowed.
* This method is similar to Java 7 <code>java.nio.file.Path.moveTo</code>.
* Rename a file if this is allowed. This method is similar to Java 7
* <code>java.nio.file.Files.move</code>.
*
* @param oldName the old fully qualified file name
* @param newName the new fully qualified file name
* @param source the old fully qualified file name
* @param target the new fully qualified file name
*/
public static void moveTo(String oldName, String newName) {
FilePath.get(oldName).moveTo(FilePath.get(newName));
public static void move(String source, String target) {
FilePath.get(source).moveTo(FilePath.get(target), false);
}
/**
* Rename a file if this is allowed, and try to atomically replace an
* existing file. This method is similar to Java 7
* <code>java.nio.file.Files.move</code>.
*
* @param source the old fully qualified file name
* @param target the new fully qualified file name
*/
public static void moveAtomicReplace(String source, String target) {
FilePath.get(source).moveTo(FilePath.get(target), true);
}
/**
......
......@@ -255,7 +255,7 @@ public class TestFileSystem extends TestBase {
stat.execute(
"create table test(id int primary key, name varchar) " +
"as select x, space(10000) from system_range(1, 100)");
stat.execute("shutdown defrag");
// stat.execute("shutdown defrag");
conn.close();
Backup.execute(dir + "/test.zip", dir, "", true);
DeleteDbFiles.execute("split:" + dir, "test", true);
......@@ -350,28 +350,34 @@ public class TestFileSystem extends TestBase {
}
private void testReadOnly(final String f) throws IOException {
new AssertThrows(IOException.class) { @Override
new AssertThrows(IOException.class) {
@Override
public void test() throws IOException {
FileUtils.newOutputStream(f, false);
}};
new AssertThrows(DbException.class) { @Override
new AssertThrows(DbException.class) {
@Override
public void test() {
FileUtils.moveTo(f, f);
FileUtils.move(f, f);
}};
new AssertThrows(DbException.class) { @Override
new AssertThrows(DbException.class) {
@Override
public void test() {
FileUtils.moveTo(f, f);
FileUtils.move(f, f);
}};
new AssertThrows(IOException.class) { @Override
new AssertThrows(IOException.class) {
@Override
public void test() throws IOException {
FileUtils.createTempFile(f, ".tmp", false, false);
}};
final FileChannel channel = FileUtils.open(f, "r");
new AssertThrows(IOException.class) { @Override
new AssertThrows(IOException.class) {
@Override
public void test() throws IOException {
channel.write(ByteBuffer.allocate(1));
}};
new AssertThrows(IOException.class) { @Override
new AssertThrows(IOException.class) {
@Override
public void test() throws IOException {
channel.truncate(0);
}};
......@@ -413,11 +419,13 @@ public class TestFileSystem extends TestBase {
FileUtils.delete(fileName);
}
if (FileUtils.createFile(fileName)) {
new AssertThrows(DbException.class) { @Override
new AssertThrows(DbException.class) {
@Override
public void test() {
FileUtils.createDirectory(fileName);
}};
new AssertThrows(DbException.class) { @Override
new AssertThrows(DbException.class) {
@Override
public void test() {
FileUtils.createDirectories(fileName + "/test");
}};
......@@ -432,17 +440,19 @@ public class TestFileSystem extends TestBase {
FileUtils.delete(fileName);
}
if (FileUtils.createFile(fileName)) {
FileUtils.moveTo(fileName, fileName2);
FileUtils.move(fileName, fileName2);
FileUtils.createFile(fileName);
new AssertThrows(DbException.class) { @Override
new AssertThrows(DbException.class) {
@Override
public void test() {
FileUtils.moveTo(fileName2, fileName);
FileUtils.move(fileName2, fileName);
}};
FileUtils.delete(fileName);
FileUtils.delete(fileName2);
new AssertThrows(DbException.class) { @Override
new AssertThrows(DbException.class) {
@Override
public void test() {
FileUtils.moveTo(fileName, fileName2);
FileUtils.move(fileName, fileName2);
}};
}
}
......@@ -454,27 +464,33 @@ public class TestFileSystem extends TestBase {
}
if (FileUtils.createFile(fileName)) {
final FileChannel channel = FileUtils.open(fileName, "rw");
new AssertThrows(UnsupportedOperationException.class) { @Override
new AssertThrows(UnsupportedOperationException.class) {
@Override
public void test() throws IOException {
channel.map(MapMode.PRIVATE, 0, channel.size());
}};
new AssertThrows(UnsupportedOperationException.class) { @Override
new AssertThrows(UnsupportedOperationException.class) {
@Override
public void test() throws IOException {
channel.read(new ByteBuffer[]{ByteBuffer.allocate(10)}, 0, 0);
}};
new AssertThrows(UnsupportedOperationException.class) { @Override
new AssertThrows(UnsupportedOperationException.class) {
@Override
public void test() throws IOException {
channel.write(new ByteBuffer[]{ByteBuffer.allocate(10)}, 0, 0);
}};
new AssertThrows(UnsupportedOperationException.class) { @Override
new AssertThrows(UnsupportedOperationException.class) {
@Override
public void test() throws IOException {
channel.transferFrom(channel, 0, 0);
}};
new AssertThrows(UnsupportedOperationException.class) { @Override
new AssertThrows(UnsupportedOperationException.class) {
@Override
public void test() throws IOException {
channel.transferTo(0, 0, channel);
}};
new AssertThrows(UnsupportedOperationException.class) { @Override
new AssertThrows(UnsupportedOperationException.class) {
@Override
public void test() throws IOException {
channel.lock();
}};
......@@ -566,8 +582,8 @@ public class TestFileSystem extends TestBase {
assertEquals(1, list.size());
assertTrue(list.get(0).endsWith("test"));
IOUtils.copyFiles(fsBase + "/test", fsBase + "/test3");
FileUtils.moveTo(fsBase + "/test3", fsBase + "/test2");
FileUtils.moveTo(fsBase + "/test2", fsBase + "/test2");
FileUtils.move(fsBase + "/test3", fsBase + "/test2");
FileUtils.move(fsBase + "/test2", fsBase + "/test2");
assertTrue(!FileUtils.exists(fsBase + "/test3"));
assertTrue(FileUtils.exists(fsBase + "/test2"));
assertEquals(10000, FileUtils.size(fsBase + "/test2"));
......
......@@ -185,9 +185,9 @@ public class FilePathDebug extends FilePathWrapper {
}
@Override
public void moveTo(FilePath newName) {
public void moveTo(FilePath newName, boolean atomicReplace) {
trace(name, "moveTo", unwrap(((FilePathDebug) newName).name));
super.moveTo(newName);
super.moveTo(newName, atomicReplace);
}
@Override
......
......@@ -193,8 +193,8 @@ public class FilePathUnstable extends FilePathWrapper {
}
@Override
public void moveTo(FilePath newName) {
super.moveTo(newName);
public void moveTo(FilePath newName, boolean atomicReplace) {
super.moveTo(newName, atomicReplace);
}
@Override
......
......@@ -275,7 +275,7 @@ public class FilePathZip2 extends FilePath {
}
@Override
public void moveTo(FilePath newName) {
public void moveTo(FilePath newName, boolean atomicReplace) {
throw DbException.getUnsupportedException("write");
}
......
......@@ -233,7 +233,7 @@ public class FileShell extends Tool {
String source = getFile(list[i++]);
String target = getFile(list[i++]);
end(list, i);
FileUtils.moveTo(source, target);
FileUtils.move(source, target);
} else if ("pwd".equals(c)) {
end(list, i);
println(FileUtils.toRealPath(currentWorkingDirectory));
......
......@@ -255,7 +255,7 @@ public class FtpControl extends Thread {
boolean ok = false;
if (!readonly) {
try {
FileUtils.moveTo(fileOld, fileNew);
FileUtils.move(fileOld, fileNew);
reply(250, "Ok");
ok = true;
} catch (Exception e) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论