What is the difference between using the Enum Standardcopyoption constants?

Asked

Viewed 214 times

4

I am implementing file copy and decided to use the class Files, and one of the signatures of the method copy() is to receive 2 parameters of type Path and a third of the kind CopyOption and this is the last one I was left with doubts.

To pass this third parameter, Enum is used Standardcopyoption, and it has 3 constants:

ATOMIC_MOVE
Move the file as an Atomic file system Operation.

COPY_ATTRIBUTES
Copy Attributes to the new file.

REPLACE_EXISTING
Replace an existing file if it exists.

The last one I even understood is about rewriting files, if it already exists in the destination path but even reading the definition of the other two, caused me some doubts:

The copy itself no longer copies the attributes of the source file by default, since we are creating replica of the file in another location?

What the ATOMIC_MOVE does otherwise than the common copy?

1 answer

5


How flags are used

All flags are passed to the file copy implementation specific to each operating system.

For example, in the case of UNIX-based systems, the class UnixCopyFile uses the options to determine whether to perform certain additional operations.

Relevant section of the class UnixCopyFile:

static Flags fromMoveOptions(CopyOption... options) {
    Flags flags = new Flags();
    for (CopyOption option: options) {
        if (option == StandardCopyOption.ATOMIC_MOVE) {
            flags.atomicMove = true;
            continue;
        }
        if (option == StandardCopyOption.REPLACE_EXISTING) {
            flags.replaceExisting = true;
            continue;
        }
        if (option == LinkOption.NOFOLLOW_LINKS) {
            // ignore
            continue;
        }
        if (option == null)
            throw new NullPointerException();
        throw new UnsupportedOperationException("Unsupported copy option");
    }

    // a move requires that all attributes be copied but only fail if
    // the basic attributes cannot be copied
    flags.copyBasicAttributes = true;
    flags.copyPosixAttributes = true;
    flags.copyNonPosixAttributes = true;
    flags.failIfUnableToCopyBasic = true;
    return flags;
}

Attribute copy

Specifically on COPY_ATTRIBUTES, usually copying a file means creating a new one in the destination location and copying the content.

File attributes such as last access date, modification date and permissions usually receive the default values of a new file.

Therefore, such a flag causes the new file to receive the same values in those attributes. Note that this is not always what we want. Copying only a few times has the complete sense of replicating in practice.

Relevant section of the class UnixCopyFile:

// copy owner/permissions
if (flags.copyPosixAttributes) {
    try {
        fchown(fo, attrs.uid(), attrs.gid());
        fchmod(fo, attrs.mode());
    } catch (UnixException x) {
        if (flags.failIfUnableToCopyPosix)
            x.rethrowAsIOException(target);
    }
}
// copy non POSIX attributes (depends on file system)
if (flags.copyNonPosixAttributes) {
    source.getFileSystem().copyNonPosixAttributes(fi, fo);
}
// copy time attributes
if (flags.copyBasicAttributes) {
    try {
        futimes(fo,
                attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
                attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
    } catch (UnixException x) {
        if (flags.failIfUnableToCopyBasic)
            x.rethrowAsIOException(target);
    }
}

Atomic operations

The flag ATOMIC_MOVE does not actually apply to copy operations, despite the name of the enum, but while moving a file.

In the case of UNIX-based systems, Java uses the command rename to change the file from the source path to the destination path, as this command is atomic.

In addition, Java code does not perform other additional operations, such as checking if the target file exists, as this would make the process subject to race conditions, i.e., non-atomic.

Relevant section of the class UnixCopyFile:

// handle atomic rename case
if (flags.atomicMove) {
    try {
        rename(source, target);
    } catch (UnixException x) {
        if (x.errno() == EXDEV) {
            throw new AtomicMoveNotSupportedException(
                source.getPathForExecptionMessage(),
                target.getPathForExecptionMessage(),
                x.errorString());
        }
        x.rethrowAsIOException(source, target);
    }
    return;
}
  • 1

    So copying operations are not atomic? This explains some problems I had, such as incomplete copies and sometimes copies with corrupted data.

  • @diegofm It is a common problem. From the point of view of a program single-threaded, the disk operations are atomic. However, other processes or threads can see a file that has just been created while you are still starting to record the data in it.

  • If you work with file integration, where one program generates the file and another program consumes, I see two options: either you make the source program warn the target program when the file is complete, or, if you use directory monitoring, record the contents in a temporary file and, only when finished, move the file to the directory where it will be read.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.