Package com.indeed.util.io
Class SafeFiles
- java.lang.Object
-
- com.indeed.util.io.SafeFiles
-
@ParametersAreNonnullByDefault public final class SafeFiles extends java.lang.ObjectUtilities for atomic (and fsync-friendly) operations on files.When possible methods on this class should be used over the ones in
Files
-
-
Method Summary
All Methods Static Methods Concrete Methods Modifier and Type Method Description static SafeOutputStreamcreateAtomicFile(java.nio.file.Path path)This is just like a lazy variation ofwrite(byte[], java.nio.file.Path).static voiddeleteIfExistsQuietly(java.nio.file.Path path)Delete a path but do not complain if it fails.static voidensureDirectoryExists(java.nio.file.Path path)Create a directory if it does not already exist.static voidfsync(java.nio.file.Path path)Fsync a single path.static intfsyncRecursive(java.nio.file.Path root)Walk a directory tree and Fsync both Directory and File inodes.static voidrename(java.nio.file.Path oldName, java.nio.file.Path newName)Perform an atomic rename of oldName -> newName and fsync the containing directory.static voidwrite(byte[] data, java.nio.file.Path path)Write the bytes to a temporary file, fsync the file, then atomically rename to the target path.static voidwriteUTF8(java.lang.String value, java.nio.file.Path path)Write the string to a temporary file, fsync the file, then atomically rename to the target path.
-
-
-
Method Detail
-
rename
public static void rename(java.nio.file.Path oldName, java.nio.file.Path newName) throws java.io.IOExceptionPerform an atomic rename of oldName -> newName and fsync the containing directory. This is only truly fsync-safe if both files are in the same directory, but it will at least try to do the right thing if the files are in different directories.- Parameters:
oldName- original filenewName- new file- Throws:
java.io.IOException- In the event that we could not rename the path.
-
ensureDirectoryExists
public static void ensureDirectoryExists(java.nio.file.Path path) throws java.io.IOExceptionCreate a directory if it does not already exist. Fails if the path exists and is NOT a directory. Will fsync the parent directory inode.- Parameters:
path- path to ensure is a directory.- Throws:
java.io.IOException- In the event that the path is not a directory.
-
fsyncRecursive
@Nonnegative public static int fsyncRecursive(java.nio.file.Path root) throws java.io.IOExceptionWalk a directory tree and Fsync both Directory and File inodes. This does NOT follow symlinks and does not attempt to fsync anything other than Directory or NormalFiles.- Parameters:
root- directory to start the traversal.- Returns:
- number of NormalFiles fsynced (not including directories).
- Throws:
java.io.IOException- in the event that we could not fsync the provided directory.
-
fsync
public static void fsync(java.nio.file.Path path) throws java.io.IOExceptionFsync a single path. Please only call this on things that are Directories or NormalFiles.- Parameters:
path- path to fsync- Throws:
java.io.IOException- in the event that we could not fsync the provided path.
-
writeUTF8
public static void writeUTF8(java.lang.String value, java.nio.file.Path path) throws java.io.IOExceptionWrite the string to a temporary file, fsync the file, then atomically rename to the target path. On error it will make a best-effort to erase the temporary file.- Parameters:
value- string value to write to file as UTF8 bytespath- path to write out to- Throws:
java.io.IOException- in the event that the data could not be written to the path.
-
write
public static void write(byte[] data, java.nio.file.Path path) throws java.io.IOExceptionWrite the bytes to a temporary file, fsync the file, then atomically rename to the target path. On error it will make a best-effort to erase the temporary file.- Parameters:
data- binary value to write to filepath- path to write out to- Throws:
java.io.IOException- in the event that the data could not be written to the path.
-
createAtomicFile
@Nonnull public static SafeOutputStream createAtomicFile(java.nio.file.Path path) throws java.io.IOException
This is just like a lazy variation ofwrite(byte[], java.nio.file.Path). It opens a temp file and proxies writes through to the underlying file.Upon calling
SafeOutputStream.commit()the rest of the safety behaviors kick in:- flush
- fsync temp file
- close temp file
- atomic rename temp file to desired filename
- fsync parent directory
On error it will make a best-effort to erase the temporary file.
If you call
SafeOutputStream.close()without callingSafeOutputStream.commit()the atomic write is aborted and cleaned up.It is safe to call
SafeOutputStream.close()} afterSafeOutputStream.commit()so that try-with-resources works.The returned
SafeOutputStreamis NOT safe for calls from multiple threads.- Parameters:
path- final desired output path- Returns:
- handle to opened temp file
- Throws:
java.io.IOException- in the event that the file could not be created.
-
deleteIfExistsQuietly
public static void deleteIfExistsQuietly(java.nio.file.Path path)
Delete a path but do not complain if it fails.- Parameters:
path- path to delete
-
-