java - 如何可靠地检测目录的原子移动因目标已存在而失败

标签 java filesystems java.nio.file

在服务器中,我在第一次访问资源时初始化该资源的工作目录。可能存在对服务器的多个进程处理的资源的并行请求,这意味着我需要注意没有一个进程看到部分初始化的工作目录。解决方案是在临时同级目录中初始化工作目录,然后使用 Files.moveStandardCopyOption.ATOMIC_MOVE 将其移动到最终位置。

如果两个进程同时初始化工作目录,则第二个原子移动会失败。这实际上并不是一个问题,因为工作目录已正确初始化,因此第二个进程只需丢弃它创建的临时目录并继续。

我尝试使用以下代码来执行此操作:

private void initalizeWorkDirectory(final Resource resource) throws IOException {
    File workDir = resource.getWorkDirectory();
    if (!workDir.exists()) {
        File tempDir = createTemporarySibligDirectory(workDir);
        try {
            fillWorkDirectory(tempDir, resource);
            Files.move(tempDir.toPath(), workDir.toPath(), StandardCopyOption.ATOMIC_MOVE);
        } catch (FileAlreadyExistsException e) {
            // do some logging
        } finally {
            FileUtils.deleteQuietly(tempDir);
        }
    }
}

但是我注意到,仅捕获 FileAlreadyExistsException 似乎还不够。如果发生移动冲突,还会引发其他异常。我不只是想捕获所有异常,因为这可能隐藏真正的问题。

那么,是否有一种方法可以从 Files.move 抛出的异常中可靠地检测到目录的原子移动因目标目录已存在而失败?

最佳答案

通过观察异常并查看 Windows 和 Linux 的 FileSystemProvider 实现,我们发现了以下内容:

  • 在 Windows 上,尝试将目录移动到现有目录时会引发 AccessDeniedException
  • 在 Unix(及其相关系统)上,会引发通用 FileSystemException 并附带一条对应于 errno.h constant ENOTEMPTY 的消息。 .

根据这些实现细节进行编程时,可以很好地检测目录移动冲突,而不会带来太多隐藏其他问题的危险。

以下代码实现了一个原子移动,在发生移动冲突时始终抛出 FileAlreadyExistsException:

import java.io.File;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;

public class AtomicMove {
    private static final String ENOTEMPTY = "Directory not empty";

    public static void move(final File source, final File target) throws FileAlreadyExistsException, IOException {
        try {
            Files.move(source.toPath(), target.toPath(), StandardCopyOption.ATOMIC_MOVE);

        } catch (AccessDeniedException e) {
            // directory move collision on Windows
            throw new FileAlreadyExistsException(source.toString(), target.toString(), e.getMessage());

        } catch (FileSystemException e) {
            if (ENOTEMPTY.equals(e.getReason())) {
                // directory move collision on Unix
                throw new FileAlreadyExistsException(source.toString(), target.toString(), e.getMessage());
            } else {
                // other problem
                throw e;
            }
        }
    }
}

关于java - 如何可靠地检测目录的原子移动因目标已存在而失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30755070/

相关文章:

java - 在拨号器应用程序顶部检测来电并在后台打开服务

java - 如何从 eclipse e4 中的处理程序执行方法获取图标

java - 为什么运行时依赖的类在编译时可用?

java - JMSListener 选择器不工作

linux - LVM Linux。我的任务中的 LE 是什么意思?可能是错字

c - 如何在c中直接写入磁盘

java - SystemFile.getLocal() 的 jar 文件是什么?

java - Jackson 如何自动反序列化 java.nio.file.Path?

Java读取文件名并按升序存储

java - 是否有一个(Java 7)文件系统,其 Path .isAbsolute() 但有一个空根?