What is the difference between using the StandardCopyOption enum constants?

4

I'm implementing copy of files and decided to use the Files , and one of the method signatures copy() " is to receive 2 parameters of type Path and a third of type CopyOption and it is in this last one that I had doubts.

To pass this third parameter, use the 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 I even understood that it is about rewriting files, if it already exists in the target path but even reading the definition of the other two, it generated some doubts:

Does the copy itself no longer copy the attributes of the source file by default, since we are creating a replica of the file somewhere else?

What does ATOMIC_MOVE do other than common copy?

    
asked by anonymous 07.12.2016 / 23:49

1 answer

5

How flags are used

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

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

Relevant class excerpt 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;
}

Copy attributes

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

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

Therefore, such flag causes the new file to receive the same values in these attributes. Note that this is not always what we want. Copying rarely has the full sense of replicating in practice.

Relevant class excerpt 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 ATOMIC_MOVE flag does not actually apply to copy operations, despite the name of enum , but when moving a file.

For UNIX-based systems, Java uses the command rename to change the file from the source path to the destination path because this command is atomic.

In addition, the Java code does not perform any additional operations, such as verifying that the destination file exists, as this would make the process subject to non-atomic running conditions.

Relevant class excerpt 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;
}
    
08.12.2016 / 04:28