Class Files
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 theNOFOLLOW_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.-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static classAFileVisitorimplementation that copies a directory tree.static enumCopyOptionvalues accepted byFiles.static classThrown to indicate that a file copy was attempted outside of the originFileStore.private static classTask to perform a background deletion.private static final classFileStoreimplementation used internally for paths for which aFileStorecannot be obtained. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final Set<CopyOption>CopyOptionvalues accepted bycopy(Path, Path, CopyOption...).private static final Durationprivate static final longprivate static final ExecutorServiceExecutor service for background deletion tasks.private static final ThreadLocal<Map<Path,Path>> private static final intThedos:attributesvalue indicating the file object is a directory.private static final ThreadLocal<Map<Path,FileStore>> private static final booleanprivate static final org.slf4j.Loggerprivate static final intprivate static final Durationstatic final DurationDeprecated.there is now no minimum time limit for operationsprivate static final SecureRandomIdentifies theFileSystemException.getReason()messages for which file operations that warrant a retry. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate static voidcheckCopyOptions(Set<? extends CopyOption> acceptedOptions, Set<? extends CopyOption> specifiedOptions) Verifies that only acceptedCopyOptions have been specified.static longcopy(InputStream stream, Path target, CopyOption... options) Copies the bytes from anInputStreamto the specified target file.static Pathcopy(Path source, Path target, CopyOption... options) Copies a file or directory.private static PathcopyInternal(Path source, Path target, CopyOption[] options) Recursively, internally-called copy method.static PathcreateFile(Path path, FileAttribute<?>... attrs) Creates a file at the specified path retrying the operation if anAccessDeniedExceptionis raised.static PathcreateFile(Path path, Duration retryTimeLimit, FileAttribute<?>... attrs) Creates a file at the specified path retrying the operation if anAccessDeniedExceptionis raised.private static voidcreateWindowsDirectorySymbolicLink(Path link, Path target, FileAttribute<?>... attrs) Creates a Windows directory link.static voidDeletes the file system path specified by first renaming the file then performing the delete.static voidDeletes the file system path specified by first renaming the file then performing the delete.static voidDeletes the file system path specified by first renaming the file then performing the delete.static booleandeleteIfExists(Path path) Deletes the file system path specified if it exists.static voiddeleteTree(Path path) Deletes the file system tree beginning at the specified path.static voiddeleteTree(Path path, Duration renameTimeLimit) Deletes the file system tree beginning at the specified path.static voiddeleteTree(Path path, Duration renameTimeLimit, Runnable progressHelper) Deletes the file system tree beginning at the specified path.private static voiddeleteTreeWithBackgroundRetry(Path path, boolean retryDirNotEmpty, Runnable progressHelper) Attempts to delete a file/directory retrying in background in the event of a retryable failure.private static voiddeleteTreeWithRetry(Path path, boolean retryDirNotEmpty, Duration renameTimeLimit, Runnable progressHelper) Delete the file or directory tree using a rename/delete scheme with retry forFileSystemExceptioninstances indicating interference from temporary access by other processes.private static CopyOption[]effectiveCopyOptions(Set<CopyOption> copyOptions) private static Stringformat(BasicFileAttributes attributes) private static FileStoregetFileStore(Path path) Gets theFileStorefor the path provided.private static longoperationDelay(Duration renameTimeLimit) private static StringrandomName(String prefix) static Pathrelocate(Path source, Path target, CopyOption... options) Moves (relocates) the source to the target location.static PathRename the file or directory with retry forFileSystemExceptioninstances indicating interference from temporary access by other processes.static PathRename the file or directory with retry forFileSystemExceptioninstances indicating interference from temporary access by other processes.static PathRename the file or directory with retry forFileSystemExceptioninstances indicating interference from temporary access by other processes.private static PathretryingRename(Path originalPath, Supplier<String> targetNameSupplier, Duration renameTimeLimit, Runnable progressHelper) Attempts to rename the specified path to a new name relative to the parent directory.private static PathretryingRenamePath(Path originalPath, Supplier<Path> renamePathSupplier, Duration renameTimeLimit, Runnable progressHelper) Attempts to rename the specified path to a new path.private static voidtreeDelete(Path path) Deletes the file or directory tree retrying for selectFileSystemExceptionand, optionally, aDirectoryNotEmptyException.
-
Field Details
-
LOGGER
private static final org.slf4j.Logger LOGGER -
IS_WINDOWS
private static final boolean IS_WINDOWS -
MINIMUM_OPERATION_REPEAT_DELAY
-
MAXIMUM_OPERATION_ATTEMPTS
private static final int MAXIMUM_OPERATION_ATTEMPTS- See Also:
-
DEFAULT_OPERATION_TIME_LIMIT_MILLIS
private static final long DEFAULT_OPERATION_TIME_LIMIT_MILLIS- See Also:
-
DEFAULT_OPERATION_TIME_LIMIT
-
MINIMUM_TIME_LIMIT
Deprecated.there is now no minimum time limit for operationsSpecifies the minimum value for the delay indelete(Path, Duration),deleteTree(Path, Duration), andrename(Path, Path, Duration). -
RETRY_REASONS
Identifies theFileSystemException.getReason()messages for which file operations that warrant a retry. -
FILE_ATTRIBUTE_DIRECTORY
private static final int FILE_ATTRIBUTE_DIRECTORYThedos:attributesvalue indicating the file object is a directory. This is used to detect a directory link which Windows handles differently from a file link. (Java does not expose the distinction between the two.) -
ACCEPTED_OPTIONS_COPY
CopyOptionvalues accepted bycopy(Path, Path, CopyOption...). -
DELETE_EXECUTOR
Executor service for background deletion tasks. This executor service uses only one daemon thread which is permitted to die when idle. -
random
-
FILE_STORE_CACHE
-
DRIVE_SUBSTITUTIONS
-
-
Constructor Details
-
Files
private Files()
-
-
Method Details
-
createFile
Creates a file at the specified path retrying the operation if anAccessDeniedExceptionis 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 createattrs- optional list of attributed to set when creating the file- Returns:
- the path of the created file
- Throws:
FileAlreadyExistsException- if a file atpathalready existsAccessDeniedException- if access topathis persistently denied; on Windows, this is thrown ifpathrefers to an existing directoryIOException- 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 anAccessDeniedExceptionis 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 createretryTimeLimit- the amount of time permitted for retrying the createattrs- optional list of attributed to set when creating the file- Returns:
- the path of the created file
- Throws:
FileAlreadyExistsException- if a file atpathalready existsAccessDeniedException- if access topathis persistently denied; on Windows, this is thrown ifpathrefers to an existing directoryIOException- if an I/O exception occursIllegalArgumentException- ifretryTimeLimitis negative- See Also:
-
rename
Rename the file or directory with retry forFileSystemExceptioninstances 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 renametarget- the new name for the file/directory; a relative path is resolved as a sibling oforigin- Returns:
- the path of the target
- Throws:
AtomicMoveNotSupportedException- iftargetis an absolute path or relative path not on the same device/root asoriginalPath; this exception indicates that a copy/delete needs to be done in place of a renameFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIOException- if the rename or delete failsSecurityException- if permission to access the files/directories involved is not sufficient
-
rename
Rename the file or directory with retry forFileSystemExceptioninstances 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 renametarget- the new name for the file/directory; a relative path is resolved as a sibling oforiginrenameTimeLimit- the time limit to apply to renaming the file/directory before deletion- Returns:
- the path of the target
- Throws:
AtomicMoveNotSupportedException- iftargetis an absolute path or relative path not on the same device/root asoriginalPath; this exception indicates that a copy/delete needs to be done in place of a renameFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIllegalArgumentException- ifrenameTimeLimitis not at least twice the operation repeat delayIOException- if the rename or delete failsNullPointerException- iforigin,target, orrenameTimeLimitis nullSecurityException- 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 forFileSystemExceptioninstances 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
progressHelperwill 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 renametarget- the new name for the file/directory; a relative path is resolved as a sibling oforiginrenameTimeLimit- the time limit to apply to renaming the file/directory before deletionprogressHelper- a helper task run between retry attempts- Returns:
- the path of the target
- Throws:
AtomicMoveNotSupportedException- iftargetis an absolute path or relative path not on the same device/root asoriginalPath; this exception indicates that a copy/delete needs to be done in place of a renameFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIllegalArgumentException- ifrenameTimeLimitis negativeIOException- if the rename or delete failsNullPointerException- iforigin,target, orrenameTimeLimitis nullSecurityException- if permission to access the files/directories involved is not sufficient
-
deleteTree
Deletes the file system tree beginning at the specified path. Ifpathis a file or a symbolic link, only that file/link is deleted; ifpathis 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 interruptedIOException- if the tree deletion failsNullPointerException- ifpathis nullSecurityException- if permission to access the file/directory to delete is not sufficient- See Also:
-
deleteTree
Deletes the file system tree beginning at the specified path. Ifpathis a file or a symbolic link, only that file/link is deleted; ifpathis 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 deleterenameTimeLimit- the time limit to apply to renaming the file/directory before deletion- Throws:
IllegalArgumentException- ifrenameTimeLimitis negativeFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIOException- if the tree deletion failsNullPointerException- ifpathorrenameTimeLimitis nullSecurityException- 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. Ifpathis a file or a symbolic link, only that file/link is deleted; ifpathis 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
progressHelperwill 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 deleterenameTimeLimit- the time limit to apply to renaming the file/directory before deletionprogressHelper- a helper task run between retry attempts- Throws:
IllegalArgumentException- ifrenameTimeLimitis negativeFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIOException- if the tree deletion failsNullPointerException- ifpathorrenameTimeLimitis nullSecurityException- if permission to access the file/directory to delete is not sufficient- See Also:
-
delete
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 emptyFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIOException- if deletion failedNullPointerException- ifpathis nullSecurityException- if permission to access the file/directory to delete is not sufficient- See Also:
-
delete
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 deleterenameTimeLimit- the time limit to apply to renaming the file/directory before deletion- Throws:
DirectoryNotEmptyException- if the directory to delete is not emptyFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIllegalArgumentException- ifrenameTimeLimitis negativeIOException- if deletion failedNullPointerException- ifpathorrenameTimeLimitis nullSecurityException- 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
progressHelperwill 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 deleterenameTimeLimit- the time limit to apply to renaming the file/directory before deletionprogressHelper- a helper task run between retry attempts- Throws:
DirectoryNotEmptyException- if the directory to delete is not emptyFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIllegalArgumentException- ifrenameTimeLimitis negativeIOException- if deletion failedNullPointerException- ifpath,renameTimeLimitis nullSecurityException- if permission to access the file/directory to delete is not sufficient- See Also:
-
deleteIfExists
Deletes the file system path specified if it exists. This method callsdelete(Path)and handles theNoSuchFileExceptionthrown 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:
trueif the file was deleted as the result of this call;falseotherwise- Throws:
IOException- if deletion failed- See Also:
-
copy
Copies a file or directory.Use the
copymethod instead ofFiles.move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...)for improved handling ofAccessDeniedExceptionand other selectFileSystemExceptionthrown due to interference from system tasks.The following options are supported:
Option Description REPLACE_EXISTINGThe target is removed at the beginning of the copy operation. Unlike Files.copy(Path, Path, CopyOption...), whenREPLACE_EXISTINGis specified, this method deletes the directory tree by callingdeleteTree(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, calldelete(Path, Duration)before calling this method.COPY_ATTRIBUTESAttempts 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_LINKSSymbolic 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...).RECURSIVECopies the directory tree using a pre-order, depth-first traversal If optionsdoes not containNOFOLLOW_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. IfNOFOLLOW_LINKSis 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,
copyoperates asFiles.copycopying only the directory and not its content.NOSPAN_FILESTORESConstrains the copy operation files contained with the FileStoreof thesourcepath. An attempt to copy a file/directory from aFileStorethat is not the sourceFileStoreresults in aFiles.FileStoreConstraintException. Copying of links to files and directories is not restricted.DEEP_COPYChanges the linking behavior described in RECURSIVEto 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 theNOFOLLOW_LINKSoption by accounts not having permissions to create symbolic links may result in aFileSystemExceptionand 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 madetarget- the file/directory to which the copy is made; must not exist unlessStandardCopyOption.REPLACE_EXISTINGis specified inoptions- options governing the copy operation- Returns:
- the path of the target
- Throws:
DirectoryNotEmptyException- iftargetis a non-empty directoryFileAlreadyExistsException- iftargetexists andREPLACE_EXISTINGis not specifiedFiles.FileStoreConstraintException- ifNOSPAN_FILESTORESis specified and an attempt to copy a non-local file is madeFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIOException- if the copy operation failsNullPointerException- ifsourceortargetis nullUnsupportedOperationException- ifoptionscontains an unsupported valueSecurityException- if permission to access the source and target file/directory is not sufficient- See Also:
-
copyInternal
Recursively, internally-called copy method.- Parameters:
source- the file/directory from which the copy is madetarget- the file/directory to which the copy is made; must not exist unlessStandardCopyOption.REPLACE_EXISTINGis specified inoptions- options governing the copy operation- Returns:
- the path of the target
- Throws:
IOException- if the copy operation failsNullPointerException- ifsourceortargetis nullUnsupportedOperationException- ifoptionscontains an unsupported valueSecurityException- if permission to access the source and target file/directory is not sufficient
-
copy
Copies the bytes from anInputStreamto the specified target file.Use this
copymethod instead ofFiles.copy(InputStream, Path, CopyOption...)for improved handling ofAccessDeniedExceptionand other selectFileSystemExceptionthrown due to interference from system tasks.As with
java.nio.file.Files.copy(InputStream, ...), this method fails iftargetexists unlessREPLACE_EXISTINGis specified. WithREPLACE_EXISTING,targetis deleted prior to copying the input stream unlesstargetis a non-empty directory -- a non-empty directory fails.This method handles a failed target file deletion when
REPLACE_EXISTINGis specified.AccessDeniedExceptionand other exceptions related to system process interference are retried; other exceptions are rethrown.The caller is responsible for closing the input
stream.Notes
WhenREPLACE_EXISTINGis specified, this method first callsdelete(target)before attempting to open an output stream ontarget. 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 astarget. 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 anAccessDeniedExceptionor another exception being thrown.- Parameters:
stream- theInputStreamto be copied totargettarget- the path of the file into which theInputStreamis writtenoptions-REPLACE_EXISTINGcan be used to deletetargetif it exists; other options are not supported- Returns:
- the number of bytes copied
- Throws:
DirectoryNotEmptyException- iftargetis a non-empty directory andREPLACE_EXISTINGis specifiedFileAlreadyExistsException- iftargetexists andREPLACE_EXISTINGis not specifiedIOException- if the copy operation failsNullPointerException- ifstreamortargetis nullUnsupportedOperationException- ifoptionscontains an unsupported valueSecurityException- if permission to access the target file/directory is not sufficient- See Also:
-
relocate
Moves (relocates) the source to the target location. This method should be used instead of theFiles.move(Path, Path, CopyOption...)method for improved handling of file system interference likeAccessDeniedException.This method first attempts a
renameand, if that fails due to anAtomicMoveNotSupportedException, acopy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...)anddeleteTree(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 madetarget- the file/directory to which the copy is made; must not exist unlessStandardCopyOption.REPLACE_EXISTINGis specified inoptions- options governing the copy operation- Returns:
- the path of the target
- Throws:
DirectoryNotEmptyException- iftargetis a non-empty directoryFileAlreadyExistsException- iftargetexists andREPLACE_EXISTINGis not specifiedFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIOException- if the copy operation failsNullPointerException- ifsourceortargetis nullUnsupportedOperationException- ifoptionscontains an unsupported valueSecurityException- 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 forFileSystemExceptioninstances 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
progressHelperwill 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 deleteretryDirNotEmpty- enable retry forDirectoryNotEmptyExceptionrenameTimeLimit- the time limit to apply to renaming the file/directory before deletionprogressHelper- 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 interruptedIllegalArgumentException- ifrenameTimeLimitis negativeIOException- if the rename or delete failsSecurityException- 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- thePathto deleteretryDirNotEmpty- enable retry forDirectoryNotEmptyExceptionprogressHelper- a helper task run between retry attempts
-
treeDelete
Deletes the file or directory tree retrying for selectFileSystemExceptionand, optionally, aDirectoryNotEmptyException. 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 forDirectoryNotEmptyExceptionis exhausted or the retries are interruptedFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIllegalArgumentException- ifattemptsis not positive orretryDelayis negativeIOException- 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. IfFiles.move(Path, Path, CopyOption...)Files.move} throws a selectFileSystemException, the rename is retried up to theattemptslimit.- Parameters:
originalPath- the file/directory path to renametargetNameSupplier- theSupplierfrom which the new name is obtainedrenameTimeLimit- the time limit to apply to renaming the file/directory before deletionprogressHelper- a helper task run between retry attempts- Returns:
- the name to which
originalPathwas renamed - Throws:
AtomicMoveNotSupportedException- iftargetNameSupplierprovides an absolute path not on the same device/root asoriginalPath; this exception indicates that a copy/delete needs to be done in place of a renameFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedInvalidPathException- iftargetNameSupplierreturns a bad pathIOException- if the rename failedIllegalArgumentException- ifrenameTimeLimitis 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. IfFiles.move(Path, Path, CopyOption...)Files.move} throws a selectFileSystemException, the rename is retried up to theattemptslimit.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
progressHelperwill 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 renamerenamePathSupplier- theSupplierfrom which the new path is obtainedrenameTimeLimit- the time limit to apply to renaming the file/directory before deletionprogressHelper- a helper task run between retry attempts- Returns:
- the name to which
originalPathwas renamed - Throws:
AtomicMoveNotSupportedException- ifrenamePathSupplierprovides an absolute path not on the same device/root asoriginalPath; this exception indicates that a copy/delete needs to be done in place of a renameFileSystemException- for a retryable exception if the time permitted for rename attempts and retryable exception handling is exhausted or interruptedIllegalArgumentException- ifrenameTimeLimitis negativeIOException- if the rename failedIllegalArgumentException- ifrenameTimeLimitis negative- See Also:
-
operationDelay
-
checkCopyOptions
private static void checkCopyOptions(Set<? extends CopyOption> acceptedOptions, Set<? extends CopyOption> specifiedOptions) throws UnsupportedOperationException Verifies that only acceptedCopyOptions have been specified.- Parameters:
acceptedOptions- the set ofCopyOptionvalid instancesspecifiedOptions- the set ofCopyOptioninstances to check- Throws:
UnsupportedOperationException- ifspecifiedOptionscontains a value not inacceptedOptions
-
effectiveCopyOptions
-
format
-
randomName
-
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 callingdelete(Path).- Parameters:
link- the path of the symbolic link to createtarget- the target of the symbolic linkattrs- the array of attributes to set atomically when creating the symbolic link- Throws:
IOException- if an error occurs creating the link
-
getFileStore
Gets theFileStorefor the path provided. This method handles obtaining theFileStorefor a Windows SUBST assignment by invoking thesubstcommand and determining the path to which the drive is assigned. The result of theFileStoredetermination is cached untilpathbecome weakly referenced.- Parameters:
path- the path for which theFileStoreis determined- Returns:
- the
FileStoreforpath; if theFileStorecannot be determined due to aFileSystemException, aFileStoreinstance unique topathis returned - Throws:
IOException- if an I/O error occurs while determining theFileStore
-