Class SafeFiles


  • @ParametersAreNonnullByDefault
    public final class SafeFiles
    extends java.lang.Object
    Utilities 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 SafeOutputStream createAtomicFile​(java.nio.file.Path path)
      This is just like a lazy variation of write(byte[], java.nio.file.Path).
      static void deleteIfExistsQuietly​(java.nio.file.Path path)
      Delete a path but do not complain if it fails.
      static void ensureDirectoryExists​(java.nio.file.Path path)
      Create a directory if it does not already exist.
      static void fsync​(java.nio.file.Path path)
      Fsync a single path.
      static int fsyncRecursive​(java.nio.file.Path root)
      Walk a directory tree and Fsync both Directory and File inodes.
      static void rename​(java.nio.file.Path oldName, java.nio.file.Path newName)
      Perform an atomic rename of oldName -> newName and fsync the containing directory.
      static void write​(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 void writeUTF8​(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.
      • Methods inherited from class java.lang.Object

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

      • rename

        public static void rename​(java.nio.file.Path oldName,
                                  java.nio.file.Path newName)
                           throws java.io.IOException
        Perform 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 file
        newName - 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.IOException
        Create 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.IOException
        Walk 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.IOException
        Fsync 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.IOException
        Write 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 bytes
        path - 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.IOException
        Write 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 file
        path - 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 of write(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 calling SafeOutputStream.commit() the atomic write is aborted and cleaned up.

        It is safe to call SafeOutputStream.close()} after SafeOutputStream.commit() so that try-with-resources works.

        The returned SafeOutputStream is 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