Class Files


  • public final class Files
    extends java.lang.Object
    Provides sane file management operations.

    Under some operating and file systems -- Windows & HPFS, we're looking at you -- file operations such as delete and move/rename can be the victim of background or asynchronous operations. When, for example, a file is deleted and a directory immediately created in its place, the directory creation operation can fail because deletion under Windows is performed asynchronously -- the delete operation simply marks the file system node as deleted and the actual removal is an asynchronous operation. When a file is renamed, that rename can fail because the anti-virus or indexing subsystems might have the file temporarily open preventing the rename (or move/delete).

    Generally speaking, simply retrying the create, delete, or rename is sufficient to get around the problem but there may be other complications -- for example, if the rename/move needs to relocate the file/directory to another file system root a simple retry becomes potentially burdensome.

    Even though read-only operations, like file copy, should not be affected by background system tasks, a mirror for Files.copy(Path, Path, CopyOption...) is defined in this class to localize copy code in the event this assertion proves inaccurate and to provide additional capabilities -- like recursive copying.

    This class does not define a move method. Use relocate(Path, Path, CopyOption...) relocate} instead. The relocate method performs a rename or copy/delete as necessary. Recovery of a copy/delete failure is left as an exercise for the caller.

    Methods in this class retry some operations when presented with an AccessDeniedException or one of another FileSystemException instances indicating potential short-term interference from other processes.

    Caution

    Windows, by default, restricts the creation of symbolic links to administrator accounts. The use of the NOFOLLOW_LINKS option will may result in a FileSystemException and the copy will fail. See How to create Soft symbolic Link using java.nio.Files for additional detail.
    See Also:
    Files.ExtendedOption, KB 329065 Access Denied Error When You Make Code Modifications with Index Services Running, DELETE PEND == ERROR_ACCESS_DENIED
    • Constructor Summary

      Constructors 
      Modifier Constructor Description
      private Files()  
    • Method Summary

      All Methods Static Methods Concrete Methods 
      Modifier and Type Method Description
      private static void checkCopyOptions​(java.util.Set<? extends java.nio.file.CopyOption> acceptedOptions, java.util.Set<? extends java.nio.file.CopyOption> specifiedOptions)
      Verifies that only accepted CopyOptions have been specified.
      static long copy​(java.io.InputStream stream, java.nio.file.Path target, java.nio.file.CopyOption... options)
      Copies the bytes from an InputStream to the specified target file.
      static java.nio.file.Path copy​(java.nio.file.Path source, java.nio.file.Path target, java.nio.file.CopyOption... options)
      Copies a file or directory.
      private static java.nio.file.Path copyInternal​(java.nio.file.Path source, java.nio.file.Path target, java.nio.file.CopyOption[] options)
      Recursively, internally-called copy method.
      static java.nio.file.Path createFile​(java.nio.file.Path path, java.nio.file.attribute.FileAttribute<?>... attrs)
      Creates a file at the specified path retrying the operation if an AccessDeniedException is raised.
      static java.nio.file.Path createFile​(java.nio.file.Path path, java.time.Duration retryTimeLimit, java.nio.file.attribute.FileAttribute<?>... attrs)
      Creates a file at the specified path retrying the operation if an AccessDeniedException is raised.
      private static void createWindowsDirectorySymbolicLink​(java.nio.file.Path link, java.nio.file.Path target, java.nio.file.attribute.FileAttribute<?>... attrs)
      Creates a Windows directory link.
      static void delete​(java.nio.file.Path path)
      Deletes the file system path specified by first renaming the file then performing the delete.
      static void delete​(java.nio.file.Path path, java.time.Duration renameTimeLimit)
      Deletes the file system path specified by first renaming the file then performing the delete.
      static void delete​(java.nio.file.Path path, java.time.Duration renameTimeLimit, java.lang.Runnable progressHelper)
      Deletes the file system path specified by first renaming the file then performing the delete.
      static boolean deleteIfExists​(java.nio.file.Path path)
      Deletes the file system path specified if it exists.
      static void deleteTree​(java.nio.file.Path path)
      Deletes the file system tree beginning at the specified path.
      static void deleteTree​(java.nio.file.Path path, java.time.Duration renameTimeLimit)
      Deletes the file system tree beginning at the specified path.
      static void deleteTree​(java.nio.file.Path path, java.time.Duration renameTimeLimit, java.lang.Runnable progressHelper)
      Deletes the file system tree beginning at the specified path.
      private static void deleteTreeWithBackgroundRetry​(java.nio.file.Path path, boolean retryDirNotEmpty, java.lang.Runnable progressHelper)
      Attempts to delete a file/directory retrying in background in the event of a retryable failure.
      private static void deleteTreeWithRetry​(java.nio.file.Path path, boolean retryDirNotEmpty, java.time.Duration renameTimeLimit, java.lang.Runnable progressHelper)
      Delete the file or directory tree using a rename/delete scheme with retry for FileSystemException instances indicating interference from temporary access by other processes.
      private static java.nio.file.CopyOption[] effectiveCopyOptions​(java.util.Set<java.nio.file.CopyOption> copyOptions)  
      private static java.lang.String format​(java.nio.file.attribute.BasicFileAttributes attributes)  
      private static java.nio.file.FileStore getFileStore​(java.nio.file.Path path)
      Gets the FileStore for the path provided.
      private static long operationDelay​(java.time.Duration renameTimeLimit)  
      private static java.lang.String randomName​(java.lang.String prefix)  
      static java.nio.file.Path relocate​(java.nio.file.Path source, java.nio.file.Path target, java.nio.file.CopyOption... options)
      Moves (relocates) the source to the target location.
      static java.nio.file.Path rename​(java.nio.file.Path origin, java.nio.file.Path target)
      Rename the file or directory with retry for FileSystemException instances indicating interference from temporary access by other processes.
      static java.nio.file.Path rename​(java.nio.file.Path origin, java.nio.file.Path target, java.time.Duration renameTimeLimit)
      Rename the file or directory with retry for FileSystemException instances indicating interference from temporary access by other processes.
      static java.nio.file.Path rename​(java.nio.file.Path origin, java.nio.file.Path target, java.time.Duration renameTimeLimit, java.lang.Runnable progressHelper)
      Rename the file or directory with retry for FileSystemException instances indicating interference from temporary access by other processes.
      private static java.nio.file.Path retryingRename​(java.nio.file.Path originalPath, java.util.function.Supplier<java.lang.String> targetNameSupplier, java.time.Duration renameTimeLimit, java.lang.Runnable progressHelper)
      Attempts to rename the specified path to a new name relative to the parent directory.
      private static java.nio.file.Path retryingRenamePath​(java.nio.file.Path originalPath, java.util.function.Supplier<java.nio.file.Path> renamePathSupplier, java.time.Duration renameTimeLimit, java.lang.Runnable progressHelper)
      Attempts to rename the specified path to a new path.
      private static void treeDelete​(java.nio.file.Path path)
      Deletes the file or directory tree retrying for select FileSystemException and, optionally, a DirectoryNotEmptyException.
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Field Detail

      • LOGGER

        private static final org.slf4j.Logger LOGGER
      • IS_WINDOWS

        private static final boolean IS_WINDOWS
      • MINIMUM_OPERATION_REPEAT_DELAY

        private static final java.time.Duration MINIMUM_OPERATION_REPEAT_DELAY
      • MAXIMUM_OPERATION_ATTEMPTS

        private static final int MAXIMUM_OPERATION_ATTEMPTS
        See Also:
        Constant Field Values
      • DEFAULT_OPERATION_TIME_LIMIT_MILLIS

        private static final long DEFAULT_OPERATION_TIME_LIMIT_MILLIS
        See Also:
        Constant Field Values
      • DEFAULT_OPERATION_TIME_LIMIT

        private static final java.time.Duration DEFAULT_OPERATION_TIME_LIMIT
      • RETRY_REASONS

        private static final java.util.Set<java.lang.String> RETRY_REASONS
        Identifies the FileSystemException.getReason() messages for which file operations that warrant a retry.
      • ACCEPTED_OPTIONS_COPY

        private static final java.util.Set<java.nio.file.CopyOption> ACCEPTED_OPTIONS_COPY
        CopyOption values accepted by copy(Path, Path, CopyOption...).
      • DELETE_EXECUTOR

        private static final java.util.concurrent.ExecutorService DELETE_EXECUTOR
        Executor service for background deletion tasks. This executor service uses only one daemon thread which is permitted to die when idle.
      • random

        private static final java.security.SecureRandom random
      • FILE_STORE_CACHE

        private static final java.lang.ThreadLocal<java.util.Map<java.nio.file.Path,​java.nio.file.FileStore>> FILE_STORE_CACHE
      • DRIVE_SUBSTITUTIONS

        private static final java.lang.ThreadLocal<java.util.Map<java.nio.file.Path,​java.nio.file.Path>> DRIVE_SUBSTITUTIONS
    • Constructor Detail

      • Files

        private Files()
    • Method Detail

      • createFile

        public static java.nio.file.Path createFile​(java.nio.file.Path path,
                                                    java.nio.file.attribute.FileAttribute<?>... attrs)
                                             throws java.io.IOException
        Creates a file at the specified path retrying the operation if an AccessDeniedException is raised.

        This method uses the default retry time limit of 2500L milliseconds.

        A Thread.interrupt() call does not interrupt the create operation -- the create process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        path - the path of the file to create
        attrs - optional list of attributed to set when creating the file
        Returns:
        the path of the created file
        Throws:
        java.nio.file.FileAlreadyExistsException - if a file at path already exists
        java.nio.file.AccessDeniedException - if access to path is persistently denied; on Windows, this is thrown if path refers to an existing directory
        java.io.IOException - if an I/O exception occurs
        See Also:
        Files.createFile(Path, FileAttribute[])
      • createFile

        public static java.nio.file.Path createFile​(java.nio.file.Path path,
                                                    java.time.Duration retryTimeLimit,
                                                    java.nio.file.attribute.FileAttribute<?>... attrs)
                                             throws java.io.IOException
        Creates a file at the specified path retrying the operation if an AccessDeniedException is raised.

        A Thread.interrupt() call does not interrupt the create operation -- the create process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        path - the path of the file to create
        retryTimeLimit - the amount of time permitted for retrying the create
        attrs - optional list of attributed to set when creating the file
        Returns:
        the path of the created file
        Throws:
        java.nio.file.FileAlreadyExistsException - if a file at path already exists
        java.nio.file.AccessDeniedException - if access to path is persistently denied; on Windows, this is thrown if path refers to an existing directory
        java.io.IOException - if an I/O exception occurs
        java.lang.IllegalArgumentException - if retryTimeLimit is negative
        See Also:
        Files.createFile(Path, FileAttribute[])
      • rename

        public static java.nio.file.Path rename​(java.nio.file.Path origin,
                                                java.nio.file.Path target)
                                         throws java.io.IOException
        Rename the file or directory with retry for FileSystemException instances indicating interference from temporary access by other processes.

        This method uses the default retry time limit of 2500L milliseconds.

        A Thread.interrupt() call does not interrupt the rename operation -- the rename process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        origin - the path of the file/directory to rename
        target - the new name for the file/directory; a relative path is resolved as a sibling of origin
        Returns:
        the path of the target
        Throws:
        java.nio.file.AtomicMoveNotSupportedException - if target is an absolute path or relative path not on the same device/root as originalPath; this exception indicates that a copy/delete needs to be done in place of a rename
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.io.IOException - if the rename or delete fails
        java.lang.SecurityException - if permission to access the files/directories involved is not sufficient
      • rename

        public static java.nio.file.Path rename​(java.nio.file.Path origin,
                                                java.nio.file.Path target,
                                                java.time.Duration renameTimeLimit)
                                         throws java.io.IOException
        Rename the file or directory with retry for FileSystemException instances indicating interference from temporary access by other processes.

        A Thread.interrupt() call does not interrupt the rename operation -- the rename process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        origin - the path of the file/directory to rename
        target - the new name for the file/directory; a relative path is resolved as a sibling of origin
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        Returns:
        the path of the target
        Throws:
        java.nio.file.AtomicMoveNotSupportedException - if target is an absolute path or relative path not on the same device/root as originalPath; this exception indicates that a copy/delete needs to be done in place of a rename
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.lang.IllegalArgumentException - if renameTimeLimit is not at least twice the operation repeat delay
        java.io.IOException - if the rename or delete fails
        java.lang.NullPointerException - if origin, target, or renameTimeLimit is null
        java.lang.SecurityException - if permission to access the files/directories involved is not sufficient
      • rename

        public static java.nio.file.Path rename​(java.nio.file.Path origin,
                                                java.nio.file.Path target,
                                                java.time.Duration renameTimeLimit,
                                                java.lang.Runnable progressHelper)
                                         throws java.io.IOException
        Rename the file or directory with retry for FileSystemException instances indicating interference from temporary access by other processes.

        A Thread.interrupt() call does not interrupt the rename operation -- the rename process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        The progressHelper will be run between retries of the operation. This can be used by the caller to perform cleanup that may allow subsequent retry attempts to make progress. Any exception thrown by the progress helper during the rename will be propagated to the user and cause the operation to fail.

        Parameters:
        origin - the path of the file/directory to rename
        target - the new name for the file/directory; a relative path is resolved as a sibling of origin
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        progressHelper - a helper task run between retry attempts
        Returns:
        the path of the target
        Throws:
        java.nio.file.AtomicMoveNotSupportedException - if target is an absolute path or relative path not on the same device/root as originalPath; this exception indicates that a copy/delete needs to be done in place of a rename
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        java.io.IOException - if the rename or delete fails
        java.lang.NullPointerException - if origin, target, or renameTimeLimit is null
        java.lang.SecurityException - if permission to access the files/directories involved is not sufficient
      • deleteTree

        public static void deleteTree​(java.nio.file.Path path)
                               throws java.io.IOException
        Deletes the file system tree beginning at the specified path. If path is a file or a symbolic link, only that file/link is deleted; if path is a directory, the directory tree, without following links, is deleted.

        The deletion is accomplished by first renaming the file/directory and then performing the delete. The rename is performed in a manner accommodating temporary access by other processes (indexing, anti-virus) and, after renaming, the file is deleted. Because the file was first renamed, at completion of this method, the path is immediately available for re-creation.

        This method uses the default retry time limit of 2500L milliseconds when renaming the path.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        path - the file/directory path to delete
        Throws:
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.io.IOException - if the tree deletion fails
        java.lang.NullPointerException - if path is null
        java.lang.SecurityException - if permission to access the file/directory to delete is not sufficient
        See Also:
        Files.delete(Path), Files.move(Path, Path, CopyOption...)
      • deleteTree

        public static void deleteTree​(java.nio.file.Path path,
                                      java.time.Duration renameTimeLimit)
                               throws java.io.IOException
        Deletes the file system tree beginning at the specified path. If path is a file or a symbolic link, only that file/link is deleted; if path is a directory, the directory tree, without following links, is deleted.

        The deletion is accomplished by first renaming the file/directory and then performing the delete. The rename is performed in a manner accommodating temporary access by other processes (indexing, anti-virus) and, after renaming, the file is deleted. Because the file was first renamed, at completion of this method, the path is immediately available for re-creation.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        path - the file/directory path to delete
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        Throws:
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.io.IOException - if the tree deletion fails
        java.lang.NullPointerException - if path or renameTimeLimit is null
        java.lang.SecurityException - if permission to access the file/directory to delete is not sufficient
        See Also:
        Files.delete(Path), Files.move(Path, Path, CopyOption...)
      • deleteTree

        public static void deleteTree​(java.nio.file.Path path,
                                      java.time.Duration renameTimeLimit,
                                      java.lang.Runnable progressHelper)
                               throws java.io.IOException
        Deletes the file system tree beginning at the specified path. If path is a file or a symbolic link, only that file/link is deleted; if path is a directory, the directory tree, without following links, is deleted.

        The deletion is accomplished by first renaming the file/directory and then performing the delete. The rename is performed in a manner accommodating temporary access by other processes (indexing, anti-virus) and, after renaming, the file is deleted. Because the file was first renamed, at completion of this method, the path is immediately available for re-creation.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        The progressHelper will be run between retries of the operation. This can be used by the caller to perform cleanup that may allow subsequent retry attempts to make progress. Any exception thrown by the progress helper during the rename phase will be propagated to the user and cause the operation to fail. The progress helper (and anything it references or captures) will be strongly-referenced until the deletion completes - possibly in a background thread long after this method is complete.

        Parameters:
        path - the file/directory path to delete
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        progressHelper - a helper task run between retry attempts
        Throws:
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.io.IOException - if the tree deletion fails
        java.lang.NullPointerException - if path or renameTimeLimit is null
        java.lang.SecurityException - if permission to access the file/directory to delete is not sufficient
        See Also:
        Files.delete(Path), Files.move(Path, Path, CopyOption...)
      • delete

        public static void delete​(java.nio.file.Path path)
                           throws java.io.IOException
        Deletes the file system path specified by first renaming the file then performing the delete. The rename is performed in a manner to accommodate temporary access by other processes (indexing, anti-virus) and, after renaming, the file is deleted. Because the file was first renamed, at completion of this method, the path is immediately available for re-creation.

        When deleting a directory, the directory must be empty.

        This method uses the default retry time limit of 2500L milliseconds when renaming the path.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        path - the path of the file to delete
        Throws:
        java.nio.file.DirectoryNotEmptyException - if the directory to delete is not empty
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.io.IOException - if deletion failed
        java.lang.NullPointerException - if path is null
        java.lang.SecurityException - if permission to access the file/directory to delete is not sufficient
        See Also:
        Files.delete(Path), Files.move(Path, Path, CopyOption...)
      • delete

        public static void delete​(java.nio.file.Path path,
                                  java.time.Duration renameTimeLimit)
                           throws java.io.IOException
        Deletes the file system path specified by first renaming the file then performing the delete. The rename is performed in a manner to accommodate temporary access by other processes (indexing, anti-virus) and, after renaming, the file is deleted. Because the file was first renamed, at completion of this method, the path is immediately available for re-creation.

        When deleting a directory, the directory must be empty.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        path - the path of the file to delete
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        Throws:
        java.nio.file.DirectoryNotEmptyException - if the directory to delete is not empty
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        java.io.IOException - if deletion failed
        java.lang.NullPointerException - if path or renameTimeLimit is null
        java.lang.SecurityException - if permission to access the file/directory to delete is not sufficient
        See Also:
        Files.delete(Path), Files.move(Path, Path, CopyOption...)
      • delete

        public static void delete​(java.nio.file.Path path,
                                  java.time.Duration renameTimeLimit,
                                  java.lang.Runnable progressHelper)
                           throws java.io.IOException
        Deletes the file system path specified by first renaming the file then performing the delete. The rename is performed in a manner to accommodate temporary access by other processes (indexing, anti-virus) and, after renaming, the file is deleted. Because the file was first renamed, at completion of this method, the path is immediately available for re-creation.

        When deleting a directory, the directory must be empty.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        The progressHelper will be run between retries of the operation. This can be used by the caller to perform cleanup that may allow subsequent retry attempts to make progress. Any exception thrown by the progress helper during the rename phase will be propagated to the user and cause the operation to fail. The progress helper (and anything it references or captures) will be strongly-referenced until the deletion completes - possibly in a background thread long after this method is complete.

        Parameters:
        path - the path of the file to delete
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        progressHelper - a helper task run between retry attempts
        Throws:
        java.nio.file.DirectoryNotEmptyException - if the directory to delete is not empty
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        java.io.IOException - if deletion failed
        java.lang.NullPointerException - if path, renameTimeLimit is null
        java.lang.SecurityException - if permission to access the file/directory to delete is not sufficient
        See Also:
        Files.delete(Path), Files.move(Path, Path, CopyOption...)
      • deleteIfExists

        public static boolean deleteIfExists​(java.nio.file.Path path)
                                      throws java.io.IOException
        Deletes the file system path specified if it exists. This method calls delete(Path) and handles the NoSuchFileException thrown if the file does not exist.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        path - the path of the file to delete
        Returns:
        true if the file was deleted as the result of this call; false otherwise
        Throws:
        java.io.IOException - if deletion failed
        See Also:
        delete(Path)
      • copy

        public static java.nio.file.Path copy​(java.nio.file.Path source,
                                              java.nio.file.Path target,
                                              java.nio.file.CopyOption... options)
                                       throws java.io.IOException
        Copies a file or directory.

        Use the copy method instead of Files.move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) for improved handling of AccessDeniedException and other select FileSystemException thrown due to interference from system tasks.

        The following options are supported:

        Option Description
        REPLACE_EXISTING The target is removed at the beginning of the copy operation. Unlike Files.copy(Path, Path, CopyOption...), when REPLACE_EXISTING is specified, this method deletes the directory tree by calling deleteTree(Path) when replacing a directory instead of failing when the target directory is not empty. If a rename time limit other than the default is wanted, call delete(Path, Duration) before calling this method.
        COPY_ATTRIBUTES Attempts to copy the file attributes associated with each copied file/directory the target. The exact file attributes that are copied is governed by Files.copy(InputStream, Path, CopyOption...).
        NOFOLLOW_LINKS Symbolic links are not followed. If the file is a symbolic link, then the symbolic link itself, not the target of the link, is copied. See Files.copy(Path, Path, CopyOption...).
        RECURSIVE Copies the directory tree using a pre-order, depth-first traversal If options does not contain NOFOLLOW_LINKS, then links are followed when performing the copy -- link structures are not replicated. If a directory tree contains linked files, these files may be duplicated. If NOFOLLOW_LINKS is specified, link structures are replicated. If a link target is outside of the source directory tree, the a link to the original target content is created.

        If omitted, copy operates as Files.copy copying only the directory and not its content.

        NOSPAN_FILESTORES Constrains the copy operation files contained with the FileStore of the source path. An attempt to copy a file/directory from a FileStore that is not the source FileStore results in a Files.FileStoreConstraintException. Copying of links to files and directories is not restricted.
        DEEP_COPY Changes the linking behavior described in RECURSIVE to copy file content that is outside the source tree instead of linking it.

        Caution

        Windows, by default, restricts the creation of symbolic links to administrator accounts. The use of the NOFOLLOW_LINKS option by accounts not having permissions to create symbolic links may result in a FileSystemException and the copy failing. See How to create Soft symbolic Link using java.nio.Files for additional detail.
        Parameters:
        source - the file/directory from which the copy is made
        target - the file/directory to which the copy is made; must not exist unless StandardCopyOption.REPLACE_EXISTING is specified in
        options - options governing the copy operation
        Returns:
        the path of the target
        Throws:
        java.nio.file.DirectoryNotEmptyException - if target is a non-empty directory
        java.nio.file.FileAlreadyExistsException - if target exists and REPLACE_EXISTING is not specified
        Files.FileStoreConstraintException - if NOSPAN_FILESTORES is specified and an attempt to copy a non-local file is made
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.io.IOException - if the copy operation fails
        java.lang.NullPointerException - if source or target is null
        java.lang.UnsupportedOperationException - if options contains an unsupported value
        java.lang.SecurityException - if permission to access the source and target file/directory is not sufficient
        See Also:
        Files.ExtendedOption, StandardCopyOption, LinkOption, Files.copy(Path, Path, CopyOption...)
      • copyInternal

        private static java.nio.file.Path copyInternal​(java.nio.file.Path source,
                                                       java.nio.file.Path target,
                                                       java.nio.file.CopyOption[] options)
                                                throws java.io.IOException
        Recursively, internally-called copy method.
        Parameters:
        source - the file/directory from which the copy is made
        target - the file/directory to which the copy is made; must not exist unless StandardCopyOption.REPLACE_EXISTING is specified in
        options - options governing the copy operation
        Returns:
        the path of the target
        Throws:
        java.io.IOException - if the copy operation fails
        java.lang.NullPointerException - if source or target is null
        java.lang.UnsupportedOperationException - if options contains an unsupported value
        java.lang.SecurityException - if permission to access the source and target file/directory is not sufficient
      • copy

        public static long copy​(java.io.InputStream stream,
                                java.nio.file.Path target,
                                java.nio.file.CopyOption... options)
                         throws java.io.IOException
        Copies the bytes from an InputStream to the specified target file.

        Use this copy method instead of Files.copy(InputStream, Path, CopyOption...) for improved handling of AccessDeniedException and other select FileSystemException thrown due to interference from system tasks.

        As with java.nio.file.Files.copy(InputStream, ...), this method fails if target exists unless REPLACE_EXISTING is specified. With REPLACE_EXISTING, target is deleted prior to copying the input stream unless target is a non-empty directory -- a non-empty directory fails.

        This method handles a failed target file deletion when REPLACE_EXISTING is specified. AccessDeniedException and other exceptions related to system process interference are retried; other exceptions are rethrown.

        The caller is responsible for closing the input stream.

        Notes

        When REPLACE_EXISTING is specified, this method first calls delete(target) before attempting to open an output stream on target. Because of this, there is a race window between deleting the target and opening target for output during which another process may create a file or directory named as target. Handling of other process interference and pending deletion is handled during delete but no interference detection is performed while opening target for output -- interference at this time may result in an AccessDeniedException or another exception being thrown.
        Parameters:
        stream - the InputStream to be copied to target
        target - the path of the file into which the InputStream is written
        options - REPLACE_EXISTING can be used to delete target if it exists; other options are not supported
        Returns:
        the number of bytes copied
        Throws:
        java.nio.file.DirectoryNotEmptyException - if target is a non-empty directory and REPLACE_EXISTING is specified
        java.nio.file.FileAlreadyExistsException - if target exists and REPLACE_EXISTING is not specified
        java.io.IOException - if the copy operation fails
        java.lang.NullPointerException - if stream or target is null
        java.lang.UnsupportedOperationException - if options contains an unsupported value
        java.lang.SecurityException - if permission to access the target file/directory is not sufficient
        See Also:
        Files.copy(InputStream, Path, CopyOption...)
      • relocate

        public static java.nio.file.Path relocate​(java.nio.file.Path source,
                                                  java.nio.file.Path target,
                                                  java.nio.file.CopyOption... options)
                                           throws java.io.IOException
        Moves (relocates) the source to the target location. This method should be used instead of the Files.move(Path, Path, CopyOption...) method for improved handling of file system interference like AccessDeniedException.

        This method first attempts a rename and, if that fails due to an AtomicMoveNotSupportedException, a copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...) and deleteTree(java.nio.file.Path) are used.

        The following copy options are defaulted and cannot be overridden:

        A Thread.interrupt() call does not interrupt the relocation operation -- the relocation process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        Parameters:
        source - the file/directory from which the copy is made
        target - the file/directory to which the copy is made; must not exist unless StandardCopyOption.REPLACE_EXISTING is specified in
        options - options governing the copy operation
        Returns:
        the path of the target
        Throws:
        java.nio.file.DirectoryNotEmptyException - if target is a non-empty directory
        java.nio.file.FileAlreadyExistsException - if target exists and REPLACE_EXISTING is not specified
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.io.IOException - if the copy operation fails
        java.lang.NullPointerException - if source or target is null
        java.lang.UnsupportedOperationException - if options contains an unsupported value
        java.lang.SecurityException - if permission to access the source and target file/directory is not sufficient
        See Also:
        Files.ExtendedOption, StandardCopyOption, LinkOption, Files.copy(Path, Path, CopyOption...)
      • deleteTreeWithRetry

        private static void deleteTreeWithRetry​(java.nio.file.Path path,
                                                boolean retryDirNotEmpty,
                                                java.time.Duration renameTimeLimit,
                                                java.lang.Runnable progressHelper)
                                         throws java.io.IOException
        Delete the file or directory tree using a rename/delete scheme with retry for FileSystemException instances indicating interference from temporary access by other processes.

        A Thread.interrupt() call does not interrupt the delete operation -- the rename/delete process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        The progressHelper will be run between retries of the operation. This can be used by the caller to perform cleanup that may allow subsequent retry attempts to make progress.

        Parameters:
        path - the file or root of the directory to delete
        retryDirNotEmpty - enable retry for DirectoryNotEmptyException
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        progressHelper - a helper task run between retry attempts
        Throws:
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        java.io.IOException - if the rename or delete fails
        java.lang.SecurityException - if permission to access the file/directory to rename/delete is not sufficient
      • deleteTreeWithBackgroundRetry

        private static void deleteTreeWithBackgroundRetry​(java.nio.file.Path path,
                                                          boolean retryDirNotEmpty,
                                                          java.lang.Runnable progressHelper)
        Attempts to delete a file/directory retrying in background in the event of a retryable failure.
        Parameters:
        path - the Path to delete
        retryDirNotEmpty - enable retry for DirectoryNotEmptyException
        progressHelper - a helper task run between retry attempts
      • treeDelete

        private static void treeDelete​(java.nio.file.Path path)
                                throws java.io.IOException
        Deletes the file or directory tree retrying for select FileSystemException and, optionally, a DirectoryNotEmptyException. The tree is walked without following links.
        Parameters:
        path - the file or root of the directory to delete
        Throws:
        java.nio.file.DirectoryNotEmptyException - if the number of deletion attempts for DirectoryNotEmptyException is exhausted or the retries are interrupted
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.lang.IllegalArgumentException - if attempts is not positive or retryDelay is negative
        java.io.IOException - if the delete failed
      • retryingRename

        private static java.nio.file.Path retryingRename​(java.nio.file.Path originalPath,
                                                         java.util.function.Supplier<java.lang.String> targetNameSupplier,
                                                         java.time.Duration renameTimeLimit,
                                                         java.lang.Runnable progressHelper)
                                                  throws java.io.IOException
        Attempts to rename the specified path to a new name relative to the parent directory. If Files.move(Path, Path, CopyOption...) Files.move} throws a select FileSystemException, the rename is retried up to the attempts limit.
        Parameters:
        originalPath - the file/directory path to rename
        targetNameSupplier - the Supplier from which the new name is obtained
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        progressHelper - a helper task run between retry attempts
        Returns:
        the name to which originalPath was renamed
        Throws:
        java.nio.file.AtomicMoveNotSupportedException - if targetNameSupplier provides an absolute path not on the same device/root as originalPath; this exception indicates that a copy/delete needs to be done in place of a rename
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.nio.file.InvalidPathException - if targetNameSupplier returns a bad path
        java.io.IOException - if the rename failed
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        See Also:
        Files.move(Path, Path, CopyOption...)
      • retryingRenamePath

        private static java.nio.file.Path retryingRenamePath​(java.nio.file.Path originalPath,
                                                             java.util.function.Supplier<java.nio.file.Path> renamePathSupplier,
                                                             java.time.Duration renameTimeLimit,
                                                             java.lang.Runnable progressHelper)
                                                      throws java.io.IOException
        Attempts to rename the specified path to a new path. If Files.move(Path, Path, CopyOption...) Files.move} throws a select FileSystemException, the rename is retried up to the attempts limit.

        A Thread.interrupt() call does not interrupt the rename operation -- the rename process continues until it completes (successfully or with a failure) at which point the interrupt is re-asserted before control is returned to the caller.

        The progressHelper will be run between retries of the operation. This can be used by the caller to perform cleanup that may allow subsequent retry attempts to make progress.

        Parameters:
        originalPath - the file/directory path to rename
        renamePathSupplier - the Supplier from which the new path is obtained
        renameTimeLimit - the time limit to apply to renaming the file/directory before deletion
        progressHelper - a helper task run between retry attempts
        Returns:
        the name to which originalPath was renamed
        Throws:
        java.nio.file.AtomicMoveNotSupportedException - if renamePathSupplier provides an absolute path not on the same device/root as originalPath; this exception indicates that a copy/delete needs to be done in place of a rename
        java.nio.file.FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        java.io.IOException - if the rename failed
        java.lang.IllegalArgumentException - if renameTimeLimit is negative
        See Also:
        Files.move(Path, Path, CopyOption...)
      • operationDelay

        private static long operationDelay​(java.time.Duration renameTimeLimit)
      • checkCopyOptions

        private static void checkCopyOptions​(java.util.Set<? extends java.nio.file.CopyOption> acceptedOptions,
                                             java.util.Set<? extends java.nio.file.CopyOption> specifiedOptions)
                                      throws java.lang.UnsupportedOperationException
        Verifies that only accepted CopyOptions have been specified.
        Parameters:
        acceptedOptions - the set of CopyOption valid instances
        specifiedOptions - the set of CopyOption instances to check
        Throws:
        java.lang.UnsupportedOperationException - if specifiedOptions contains a value not in acceptedOptions
      • effectiveCopyOptions

        private static java.nio.file.CopyOption[] effectiveCopyOptions​(java.util.Set<java.nio.file.CopyOption> copyOptions)
      • format

        private static java.lang.String format​(java.nio.file.attribute.BasicFileAttributes attributes)
      • randomName

        private static java.lang.String randomName​(java.lang.String prefix)
      • createWindowsDirectorySymbolicLink

        private static void createWindowsDirectorySymbolicLink​(java.nio.file.Path link,
                                                               java.nio.file.Path target,
                                                               java.nio.file.attribute.FileAttribute<?>... attrs)
                                                        throws java.io.IOException
        Creates a Windows directory link. Since the JDK support for Windows symbolic linking cannot create a directory symbolic link without having an existing target, this method temporarily creates, if necessary, the directory target before creating the symbolic link. Once the link is created, the temporary directory is deleted by calling delete(Path).
        Parameters:
        link - the path of the symbolic link to create
        target - the target of the symbolic link
        attrs - the array of attributes to set atomically when creating the symbolic link
        Throws:
        java.io.IOException - if an error occurs creating the link
      • getFileStore

        private static java.nio.file.FileStore getFileStore​(java.nio.file.Path path)
                                                     throws java.io.IOException
        Gets the FileStore for the path provided. This method handles obtaining the FileStore for a Windows SUBST assignment by invoking the subst command and determining the path to which the drive is assigned. The result of the FileStore determination is cached until path become weakly referenced.
        Parameters:
        path - the path for which the FileStore is determined
        Returns:
        the FileStore for path; if the FileStore cannot be determined due to a FileSystemException, a FileStore instance unique to path is returned
        Throws:
        java.io.IOException - if an I/O error occurs while determining the FileStore