All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.sshd.common.util.io.FileSnapshot Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.sshd.common.util.io;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;

/**
 * A snapshot of file metadata that can be used to determine whether a file has been modified since the last time it was
 * read. Intended usage:
 *
 * 
 * FileSnapshot fileSnapshot = FileSnapshot.save(path);
 * byte[] content = Files.readAllBytes(path);
 * ...
 * FileSnapshot newSnapshot = oldSnapshot.reload(path);
 * if (newSnapshot == fileSnapshot) {
 *   // File was not modified
 * } else {
 *   // File may have been modified
 *   fileSnapshot = newSnapshot;
 *   content = Files.readAllBytes(path);
 * }
 * 
* *

* File modifications that occur quicker than the resolution of the system's "last modified" timestamp of a file cannot * be detected reliably. This implementation assumes a worst-case filesystem timestamp resolution of 2 seconds (as it * exists on FAT file systems). A snapshot taken within 2 seconds since the last modified time is considered "racily * clean" only: the file will be considered potentially modified even if the metadata matches. *

* * @author Apache MINA SSHD Project */ public class FileSnapshot { /** * A value indicating an unknown file size. */ public static final long UNKNOWN_SIZE = -1L; /** * A {@link FileSnapshot} describing a non-existing file. */ public static final FileSnapshot NO_FILE = new FileSnapshot(Instant.now(), null, UNKNOWN_SIZE, null); // FAT has a truly crude timestamp resolution. private static final Duration WORST_CASE_TIMESTAMP_RESOLUTION = Duration.ofMillis(2000); // File metadata private final FileTime lastModified; private final long size; private final Object fileKey; // The time the snapshot was taken; needed to determine whether it might be "racily clean" private final Instant snapTime; /** * Creates a new {@link FileSnapshot} instance. * * @param snapTime the {@link Instant} the snapshot was taken * @param lastModified the "last modified" {@link FileTime} * @param size the file size * @param fileKey the file key */ protected FileSnapshot(Instant snapTime, FileTime lastModified, long size, Object fileKey) { this.snapTime = Objects.requireNonNull(snapTime); this.lastModified = lastModified; this.size = size; this.fileKey = fileKey; } /** * Retrieves the "last modified" time as recorded in this {@link FileSnapshot}. * * @return the {@link FileTime}, may be {@code null} */ protected FileTime getLastModified() { return lastModified; } /** * Retrieves the file size as recorded in this {@link FileSnapshot}. * * @return the size, {@link #UNKNOWN_SIZE} for a snapshot of a non-existing file */ protected long getSize() { return size; } /** * Retrieves the file key as recorded in this {@link FileSnapshot}. * * @return the file key, may be {@code null} */ protected Object getFileKey() { return fileKey; } /** * Retrieves the time this {@link FileSnapshot} was taken. * * @return the {@link Instant} the snapshot was taken, never {@code null} */ protected Instant getTime() { return snapTime; } /** * Creates a new {@link FileSnapshot} for the given path. * * @param file to take the snapshot of * @param options {@link LinkOption}s to use * @return the {@link FileSnapshot}, never {@code null} * @throws IOException if an I/O error occurs */ public static FileSnapshot save(Path file, LinkOption... options) throws IOException { BasicFileAttributes attributes = null; Instant now = Instant.now(); try { attributes = Files.readAttributes(file, BasicFileAttributes.class, options); } catch (NoSuchFileException e) { return NO_FILE; } if (attributes == null) { return NO_FILE; } return new FileSnapshot(now, attributes.lastModifiedTime(), attributes.size(), attributes.fileKey()); } /** * Reload the {@link FileSnapshot} for the given file. * * @param file to take the snapshot of * @param options {@link LinkOption}s to use * @return a {@link FileSnapshot}, never {@code null}; if {@code == this}, the file may be assumed * unmodified * @throws IOException if an I/O error occurs */ public FileSnapshot reload(Path file, LinkOption... options) throws IOException { FileSnapshot newSnapshot = save(file, options); if (newSnapshot.mayBeRacilyClean()) { return newSnapshot; } return same(newSnapshot) && !mayBeRacilyClean() ? this : newSnapshot; } /** * Determines whether this {@link FileSnapshot} was taken within the file timestamp resolution of the file system * after the last modified time of the file. * * @return {@code true} if so, {@code false} otherwise */ protected boolean mayBeRacilyClean() { FileTime fTime = getLastModified(); return fTime != null && Duration.between(fTime.toInstant(), getTime()).compareTo(WORST_CASE_TIMESTAMP_RESOLUTION) <= 0; } /** * Compares the snapshots' file metadata. * * @param other {@link FileSnapshot} to compare to (should be for the same {@link Path}) * @return {@code true} if the two snapshots have the same file metadata, {@code false} otherwise */ public boolean same(FileSnapshot other) { if (this == other) { return true; } if (other == null) { return false; } return Objects.equals(getFileKey(), other.getFileKey()) && Objects.equals(getLastModified(), other.getLastModified()) && getSize() == other.getSize(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy