Class FileSnapshot

  • Direct Known Subclasses:
    PackFileSnapshot

    public class FileSnapshot
    extends java.lang.Object
    Caches when a file was last read, making it possible to detect future edits.

    This object tracks the last modified time of a file. Later during an invocation of isModified(File) the object will return true if the file may have been modified and should be re-read from disk.

    A snapshot does not "live update" when the underlying filesystem changes. Callers must poll for updates by periodically invoking isModified(File).

    To work around the "racy git" problem (where a file may be modified multiple times within the granularity of the filesystem modification clock) this class may return true from isModified(File) if the last modification time of the file is less than 3 seconds ago.

    • Field Detail

      • LOG

        private static final org.slf4j.Logger LOG
      • UNKNOWN_SIZE

        public static final long UNKNOWN_SIZE
        An unknown file size. This value is used when a comparison needs to happen purely on the lastUpdate.
        See Also:
        Constant Field Values
      • UNKNOWN_TIME

        private static final java.time.Instant UNKNOWN_TIME
      • MISSING_FILEKEY

        private static final java.lang.Object MISSING_FILEKEY
      • dateFmt

        private static final java.time.format.DateTimeFormatter dateFmt
      • DIRTY

        public static final FileSnapshot DIRTY
        A FileSnapshot that is considered to always be modified.

        This instance is useful for application code that wants to lazily read a file, but only after isModified(File) gets invoked. The returned snapshot contains only invalid status information.

      • MISSING_FILE

        public static final FileSnapshot MISSING_FILE
        A FileSnapshot that is clean if the file does not exist.

        This instance is useful if the application wants to consider a missing file to be clean. isModified(File) will return false if the file path does not exist.

      • lastModified

        private final java.time.Instant lastModified
        Last observed modification time of the path.
      • lastRead

        private volatile java.time.Instant lastRead
        Last wall-clock time the path was read.
      • cannotBeRacilyClean

        private boolean cannotBeRacilyClean
        True once lastRead is far later than lastModified.
      • size

        private final long size
        Underlying file-system size in bytes. When set to UNKNOWN_SIZE the size is not considered for modification checks.
      • fileStoreAttributeCache

        private FS.FileStoreAttributes fileStoreAttributeCache
        measured FileStore attributes
      • useConfig

        private boolean useConfig
        if true read filesystem time resolution from configuration file otherwise use fallback resolution
      • fileKey

        private final java.lang.Object fileKey
        Object that uniquely identifies the given file, or null if a file key is not available
      • file

        private final java.io.File file
      • sizeChanged

        private boolean sizeChanged
      • fileKeyChanged

        private boolean fileKeyChanged
      • lastModifiedChanged

        private boolean lastModifiedChanged
      • wasRacyClean

        private boolean wasRacyClean
      • delta

        private long delta
      • racyThreshold

        private long racyThreshold
    • Constructor Detail

      • FileSnapshot

        protected FileSnapshot​(java.io.File file)
        Record a snapshot for a specific file path.

        This method should be invoked before the file is accessed.

        Parameters:
        file - the path to remember meta data for. The path's current status information is saved.
      • FileSnapshot

        protected FileSnapshot​(java.io.File file,
                               boolean useConfig)
        Record a snapshot for a specific file path.

        This method should be invoked before the file is accessed.

        Parameters:
        file - the path to remember meta data for. The path's current status information is saved.
        useConfig - if true read filesystem time resolution from configuration file otherwise use fallback resolution
      • FileSnapshot

        private FileSnapshot​(java.time.Instant read,
                             java.time.Instant modified,
                             long size,
                             @NonNull
                             java.time.Duration fsTimestampResolution,
                             @NonNull
                             java.lang.Object fileKey)
    • Method Detail

      • save

        public static FileSnapshot save​(java.io.File path)
        Record a snapshot for a specific file path.

        This method should be invoked before the file is accessed.

        Parameters:
        path - the path to later remember. The path's current status information is saved.
        Returns:
        the snapshot.
      • saveNoConfig

        public static FileSnapshot saveNoConfig​(java.io.File path)
        Record a snapshot for a specific file path without using config file to get filesystem timestamp resolution.

        This method should be invoked before the file is accessed. It is used by FileBasedConfig to avoid endless recursion.

        Parameters:
        path - the path to later remember. The path's current status information is saved.
        Returns:
        the snapshot.
      • getFileKey

        private static java.lang.Object getFileKey​(java.nio.file.attribute.BasicFileAttributes fileAttributes)
      • save

        @Deprecated
        public static FileSnapshot save​(long modified)
        Deprecated.
        use save(Instant) instead.
        Record a snapshot for a file for which the last modification time is already known.

        This method should be invoked before the file is accessed.

        Note that this method cannot rely on measuring file timestamp resolution to avoid racy git issues caused by finite file timestamp resolution since it's unknown in which filesystem the file is located. Hence the worst case fallback for timestamp resolution is used.

        Parameters:
        modified - the last modification time of the file
        Returns:
        the snapshot.
      • save

        public static FileSnapshot save​(java.time.Instant modified)
        Record a snapshot for a file for which the last modification time is already known.

        This method should be invoked before the file is accessed.

        Note that this method cannot rely on measuring file timestamp resolution to avoid racy git issues caused by finite file timestamp resolution since it's unknown in which filesystem the file is located. Hence the worst case fallback for timestamp resolution is used.

        Parameters:
        modified - the last modification time of the file
        Returns:
        the snapshot.
      • lastModified

        @Deprecated
        public long lastModified()
        Deprecated.
        Get time of last snapshot update
        Returns:
        time of last snapshot update
      • lastModifiedInstant

        public java.time.Instant lastModifiedInstant()
        Get time of last snapshot update
        Returns:
        time of last snapshot update
      • size

        public long size()
        Returns:
        file size in bytes of last snapshot update
      • isModified

        public boolean isModified​(java.io.File path)
        Check if the path may have been modified since the snapshot was saved.
        Parameters:
        path - the path the snapshot describes.
        Returns:
        true if the path needs to be read again.
      • setClean

        public void setClean​(FileSnapshot other)
        Update this snapshot when the content hasn't changed.

        If the caller gets true from isModified(File), re-reads the content, discovers the content is identical, and equals(FileSnapshot) is true, it can use setClean(FileSnapshot) to make a future isModified(File) return false. The logic goes something like this:

         if (snapshot.isModified(path)) {
          FileSnapshot other = FileSnapshot.save(path);
          Content newContent = ...;
          if (oldContent.equals(newContent) && snapshot.equals(other))
              snapshot.setClean(other);
         }
         
        Parameters:
        other - the other snapshot.
      • waitUntilNotRacy

        public void waitUntilNotRacy()
                              throws java.lang.InterruptedException
        Wait until this snapshot's file can't be racy anymore
        Throws:
        java.lang.InterruptedException - if sleep was interrupted
      • equals

        public boolean equals​(FileSnapshot other)
        Compare two snapshots to see if they cache the same information.
        Parameters:
        other - the other snapshot.
        Returns:
        true if the two snapshots share the same information.
      • equals

        public boolean equals​(java.lang.Object obj)
        Overrides:
        equals in class java.lang.Object
      • hashCode

        public int hashCode()
        Overrides:
        hashCode in class java.lang.Object
      • wasSizeChanged

        boolean wasSizeChanged()
        Returns:
        true if FileSnapshot.isModified(File) found the file size changed
      • wasFileKeyChanged

        boolean wasFileKeyChanged()
        Returns:
        true if FileSnapshot.isModified(File) found the file key changed
      • wasLastModifiedChanged

        boolean wasLastModifiedChanged()
        Returns:
        true if FileSnapshot.isModified(File) found the file's lastModified changed
      • wasLastModifiedRacilyClean

        boolean wasLastModifiedRacilyClean()
        Returns:
        true if FileSnapshot.isModified(File) detected that lastModified is racily clean
      • lastDelta

        public long lastDelta()
        Returns:
        the delta in nanoseconds between lastModified and lastRead during last racy check
      • lastRacyThreshold

        public long lastRacyThreshold()
        Returns:
        the racyLimitNanos threshold in nanoseconds during last racy check
      • toString

        public java.lang.String toString()
        Overrides:
        toString in class java.lang.Object
      • isRacyClean

        private boolean isRacyClean​(java.time.Instant read)
      • getEffectiveRacyThreshold

        private long getEffectiveRacyThreshold()
      • isModified

        private boolean isModified​(java.time.Instant currLastModified)
      • isFileKeyChanged

        private boolean isFileKeyChanged​(java.lang.Object currFileKey)
      • isSizeChanged

        private boolean isSizeChanged​(long currSize)