2

In a directory using Java I want to check each subdirectory for a DOS-specific attribute, but I want the code to run on other file systems as well. I can do this, which seems to be what the Java API implies is the recommended approach:

try {
  DosFileAttributes attrs = Files.readAttributes(path, DosFileAttributes.class);
  //TODO check DOS attrs
} catch(UnsupportedOperationException ex) {
  //ignore the error; must be another file system
}

However that is ugly, against best practices (requires ignoring an exception), and probably less efficient because of the try/catch overhead. I'd prefer to do this (using Java 17 syntax to cast to DosFileAttributes on the fly):

BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
if(attrs instance of DosFileAttributes dosFileAttributes) {
  //TODO check DOS attrs
}

That's much cleaner. Much more understandable. Probably more efficient.

Unfortunately it's not clear to me if the API guarantees it to work (even though I see code all over the Internet assuming that the second approach will always work). In practice it would appear that OpenJDK will give me a DosFileAttributes (actually a WindowsFileAttributes) instance, even though I requested only a BasicFileAttributes, because DosFileAttributes implements BasicFileAttributes and it's just as easy to always return the same object since it works in all situations.

But does the API guarantee that? From my reading, it would seem that, because I only requested a BasicFileAttributes instance, some JDK implementation might (for various reasons, not just spite) decide to return only a BasicFileAttributes instance (perhaps it doesn't want to go lookup the DOS attributes unless it was specifically asked).

So am I stuck with the ugly and inefficient exception-based approach if I want my code to be guaranteed to work?

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272

2 Answers2

1

There is no such guaranty written anywhere. This, however, does not imply that you are stuck with using exceptions. You can use, for example

DosFileAttributeView view = Files.getFileAttributeView(path,DosFileAttributeView.class);
if(view != null) {
    DosFileAttributes attrs = view.readAttributes();
    // proceed
}

At the first glance, you could pretest with Files.getFileStore(path) .supportsFileAttributeView( DosFileAttributeView.class), but the documentation of supportsFileAttributeView says:

In the case of the default provider, this method cannot guarantee to give the correct result when the file store is not a local storage device. The reasons for this are implementation specific and therefore unspecified.

which is not very helpful. One thing you can do, is to test, whether the particular filesystem supports the DosFileAttributeView in general. If path.getFileSystem() .supportedFileAttributeViews() .contains("dos") returns false, none of the paths of this filesystem will ever support this attribute.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • I only recently was reminded of retrieving `FileAttributeView` versus retrieving `FileAttributes` dichotomy. Thanks for pointing out the discrepancy in their functioning: the former returns `null` while the latter throws an exception. This is indeed useful, if disappointing that they work differently. I wonder if getting the extra layer of indirection with `FileAttributeView` is much more efficient than just getting the attributes directly. – Garret Wilson Nov 10 '22 at 15:29
  • 1
    In case of `WindowsFileSystemProvider`, a `Files.readAttributes(path, DosFileAttributes.class)` call ends up calling `getFileAttributeView(file, DosFileAttributeView.class).readAttributes()` under the hood anyway. In case of providers not supporting dos attributes, there is no indirection, as you don’t get to the second call and a `null` test is cheaper than an exception, in most cases. – Holger Nov 10 '22 at 15:50
0

I decided to trace the source code to see what exactly is it doing (is it casting? is it a switch statement?)

Finally, I landed on LinuxFileSystemProvider's implementation for readAttributes. This may change depending, but assuming it hasn't, this does do some manual if-checking on the class.

@Override
@SuppressWarnings("unchecked")
public <A extends BasicFileAttributes> A readAttributes(Path file,
                                                        Class<A> type,
                                                        LinkOption... options)
    throws IOException
{
    if (type == DosFileAttributes.class) {
        DosFileAttributeView view =
            getFileAttributeView(file, DosFileAttributeView.class, options);
        return (A) view.readAttributes();
    } else {
        return super.readAttributes(file, type, options);
    }
}

Unfortunately, assuming that future JDK versions took this same path, you will have to stick with the old exception ignoring.

LeoDog896
  • 3,472
  • 1
  • 15
  • 40