Class Files

java.lang.Object
org.terracotta.utilities.io.Files

public final class Files extends 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:
  • Field Details

  • Constructor Details

    • Files

      private Files()
  • Method Details

    • createFile

      public static Path createFile(Path path, FileAttribute<?>... attrs) throws 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:
      FileAlreadyExistsException - if a file at path already exists
      AccessDeniedException - if access to path is persistently denied; on Windows, this is thrown if path refers to an existing directory
      IOException - if an I/O exception occurs
      See Also:
    • createFile

      public static Path createFile(Path path, Duration retryTimeLimit, FileAttribute<?>... attrs) throws 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:
      FileAlreadyExistsException - if a file at path already exists
      AccessDeniedException - if access to path is persistently denied; on Windows, this is thrown if path refers to an existing directory
      IOException - if an I/O exception occurs
      IllegalArgumentException - if retryTimeLimit is negative
      See Also:
    • rename

      public static Path rename(Path origin, Path target) throws 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:
      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
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IOException - if the rename or delete fails
      SecurityException - if permission to access the files/directories involved is not sufficient
    • rename

      public static Path rename(Path origin, Path target, Duration renameTimeLimit) throws 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:
      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
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IllegalArgumentException - if renameTimeLimit is not at least twice the operation repeat delay
      IOException - if the rename or delete fails
      NullPointerException - if origin, target, or renameTimeLimit is null
      SecurityException - if permission to access the files/directories involved is not sufficient
    • rename

      public static Path rename(Path origin, Path target, Duration renameTimeLimit, Runnable progressHelper) throws 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:
      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
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IllegalArgumentException - if renameTimeLimit is negative
      IOException - if the rename or delete fails
      NullPointerException - if origin, target, or renameTimeLimit is null
      SecurityException - if permission to access the files/directories involved is not sufficient
    • deleteTree

      public static void deleteTree(Path path) throws 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:
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IOException - if the tree deletion fails
      NullPointerException - if path is null
      SecurityException - if permission to access the file/directory to delete is not sufficient
      See Also:
    • deleteTree

      public static void deleteTree(Path path, Duration renameTimeLimit) throws 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:
      IllegalArgumentException - if renameTimeLimit is negative
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IOException - if the tree deletion fails
      NullPointerException - if path or renameTimeLimit is null
      SecurityException - if permission to access the file/directory to delete is not sufficient
      See Also:
    • deleteTree

      public static void deleteTree(Path path, Duration renameTimeLimit, Runnable progressHelper) throws 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:
      IllegalArgumentException - if renameTimeLimit is negative
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IOException - if the tree deletion fails
      NullPointerException - if path or renameTimeLimit is null
      SecurityException - if permission to access the file/directory to delete is not sufficient
      See Also:
    • delete

      public static void delete(Path path) throws 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:
      DirectoryNotEmptyException - if the directory to delete is not empty
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IOException - if deletion failed
      NullPointerException - if path is null
      SecurityException - if permission to access the file/directory to delete is not sufficient
      See Also:
    • delete

      public static void delete(Path path, Duration renameTimeLimit) throws 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:
      DirectoryNotEmptyException - if the directory to delete is not empty
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IllegalArgumentException - if renameTimeLimit is negative
      IOException - if deletion failed
      NullPointerException - if path or renameTimeLimit is null
      SecurityException - if permission to access the file/directory to delete is not sufficient
      See Also:
    • delete

      public static void delete(Path path, Duration renameTimeLimit, Runnable progressHelper) throws 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:
      DirectoryNotEmptyException - if the directory to delete is not empty
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IllegalArgumentException - if renameTimeLimit is negative
      IOException - if deletion failed
      NullPointerException - if path, renameTimeLimit is null
      SecurityException - if permission to access the file/directory to delete is not sufficient
      See Also:
    • deleteIfExists

      public static boolean deleteIfExists(Path path) throws 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:
      IOException - if deletion failed
      See Also:
    • copy

      public static Path copy(Path source, Path target, CopyOption... options) throws 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:
      DirectoryNotEmptyException - if target is a non-empty directory
      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
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IOException - if the copy operation fails
      NullPointerException - if source or target is null
      UnsupportedOperationException - if options contains an unsupported value
      SecurityException - if permission to access the source and target file/directory is not sufficient
      See Also:
    • copyInternal

      private static Path copyInternal(Path source, Path target, CopyOption[] options) throws 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:
      IOException - if the copy operation fails
      NullPointerException - if source or target is null
      UnsupportedOperationException - if options contains an unsupported value
      SecurityException - if permission to access the source and target file/directory is not sufficient
    • copy

      public static long copy(InputStream stream, Path target, CopyOption... options) throws 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:
      DirectoryNotEmptyException - if target is a non-empty directory and REPLACE_EXISTING is specified
      FileAlreadyExistsException - if target exists and REPLACE_EXISTING is not specified
      IOException - if the copy operation fails
      NullPointerException - if stream or target is null
      UnsupportedOperationException - if options contains an unsupported value
      SecurityException - if permission to access the target file/directory is not sufficient
      See Also:
    • relocate

      public static Path relocate(Path source, Path target, CopyOption... options) throws 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:
      DirectoryNotEmptyException - if target is a non-empty directory
      FileAlreadyExistsException - if target exists and REPLACE_EXISTING is not specified
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IOException - if the copy operation fails
      NullPointerException - if source or target is null
      UnsupportedOperationException - if options contains an unsupported value
      SecurityException - if permission to access the source and target file/directory is not sufficient
      See Also:
    • deleteTreeWithRetry

      private static void deleteTreeWithRetry(Path path, boolean retryDirNotEmpty, Duration renameTimeLimit, Runnable progressHelper) throws 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:
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IllegalArgumentException - if renameTimeLimit is negative
      IOException - if the rename or delete fails
      SecurityException - if permission to access the file/directory to rename/delete is not sufficient
    • deleteTreeWithBackgroundRetry

      private static void deleteTreeWithBackgroundRetry(Path path, boolean retryDirNotEmpty, 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(Path path) throws 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:
      DirectoryNotEmptyException - if the number of deletion attempts for DirectoryNotEmptyException is exhausted or the retries are interrupted
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IllegalArgumentException - if attempts is not positive or retryDelay is negative
      IOException - if the delete failed
    • retryingRename

      private static Path retryingRename(Path originalPath, Supplier<String> targetNameSupplier, Duration renameTimeLimit, Runnable progressHelper) throws 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:
      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
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      InvalidPathException - if targetNameSupplier returns a bad path
      IOException - if the rename failed
      IllegalArgumentException - if renameTimeLimit is negative
      See Also:
    • retryingRenamePath

      private static Path retryingRenamePath(Path originalPath, Supplier<Path> renamePathSupplier, Duration renameTimeLimit, Runnable progressHelper) throws 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:
      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
      FileSystemException - for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interrupted
      IllegalArgumentException - if renameTimeLimit is negative
      IOException - if the rename failed
      IllegalArgumentException - if renameTimeLimit is negative
      See Also:
    • operationDelay

      private static long operationDelay(Duration renameTimeLimit)
    • checkCopyOptions

      private static void checkCopyOptions(Set<? extends CopyOption> acceptedOptions, Set<? extends CopyOption> specifiedOptions) throws 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:
      UnsupportedOperationException - if specifiedOptions contains a value not in acceptedOptions
    • effectiveCopyOptions

      private static CopyOption[] effectiveCopyOptions(Set<CopyOption> copyOptions)
    • format

      private static String format(BasicFileAttributes attributes)
    • randomName

      private static String randomName(String prefix)
    • createWindowsDirectorySymbolicLink

      private static void createWindowsDirectorySymbolicLink(Path link, Path target, FileAttribute<?>... attrs) throws 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:
      IOException - if an error occurs creating the link
    • getFileStore

      private static FileStore getFileStore(Path path) throws 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:
      IOException - if an I/O error occurs while determining the FileStore