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