Read content of file in Java without updating last access time

240 Views Asked by At

I am looking for a way to read the content of a file in Java without updating the last access time of the file. In C on Linux this is achieved by passing the O_NOATIME flag to open, and I'm sure other languages and platforms have similar mechanisms.

I'm using Files.newInputStream which has various options that can be passed, but none that appear to match O_NOATIME.

Is there a way to read the content of a file in Java on a unix-like platform without updating the last access time of the file?

1

There are 1 best solutions below

2
On

I don't think there's a way to do this in Java (apart from implementing it natively with JNI OFC). I grepped JDK sources at Github for instances of O_NOATIME and didn't get any hits.

If you peek into the UnixChannelFactory class you can see the Flags class which, I think, lists all supported options:

protected static class Flags {
    boolean read;
    boolean write;
    boolean append;
    boolean truncateExisting;
    boolean noFollowLinks;
    boolean create;
    boolean createNew;
    boolean deleteOnClose;
    boolean sync;
    boolean dsync;
    boolean direct;

    static Flags toFlags(Set<? extends OpenOption> options) {
        Flags flags = new Flags();
        for (OpenOption option: options) {
            if (option instanceof StandardOpenOption) {
                switch ((StandardOpenOption)option) {
                    case READ : flags.read = true; break;
                    case WRITE : flags.write = true; break;
                    case APPEND : flags.append = true; break;
                    case TRUNCATE_EXISTING : flags.truncateExisting = true; break;
                    case CREATE : flags.create = true; break;
                    case CREATE_NEW : flags.createNew = true; break;
                    case DELETE_ON_CLOSE : flags.deleteOnClose = true; break;
                    case SPARSE : /* ignore */ break;
                    case SYNC : flags.sync = true; break;
                    case DSYNC : flags.dsync = true; break;
                    default: throw new UnsupportedOperationException();
                }
                continue;
            }
            if (option == LinkOption.NOFOLLOW_LINKS && O_NOFOLLOW != 0) {
                flags.noFollowLinks = true;
                continue;
            }

            if (ExtendedOptions.DIRECT.matches(option)) {
                flags.direct = true;
                continue;
            }

            if (option == null)
                throw new NullPointerException();
           throw new UnsupportedOperationException(option + " not supported");
        }
        return flags;
    }
}

As a workaround you can revert the last access time after accessing the file:

// Create a file and write some content (empty files don't have their last access time updated after accessing - at least on Windows)
Path file = Files.createTempFile(null, null);
Files.write(file, Arrays.asList("line 1", "line 2"));

// Retrieve the last access time attribute before accessing the file    
FileTime lastAccessTimeBeforeAccessing = getLastAccessTime(file);

// Access the file
Files.readAllLines(file);

// Retrieve the last access time attribute after accessing the file 
FileTime lastAccessTimeAfterAccessing = getLastAccessTime(file);

// Revert the last access time
Files.setAttribute(file, "basic:lastAccessTime", lastAccessTimeBeforeAccessing);

// Retrieve the last access time attribute after reverting it   
FileTime lastAccessTimeAfterReverting = getLastAccessTime(file);

// at this point:
// * lastAccessTimeBeforeAccessing.equals(lastAccessTimeAfterAccessing) is false
// * lastAccessTimeBeforeAccessing.equals(lastAccessTimeAfterReverting) is true

The getLastAccessTime function is:

private static FileTime getLastAccessTime(Path file) throws IOException {
    BasicFileAttributes basicFileAttributes = Files.readAttributes(
        file, BasicFileAttributes.class);
    return basicFileAttributes.lastAccessTime();
}