2

Checking for an existing file using any available method is usually very fast, but if the file path includes an inaccessible (network) drive, the check takes several seconds.

Checking for a non-existent drive is no problem ("R:" in the code sample), but checking a known drive that is no longer accessible ("S:" in the code sample) takes very long time.

Edit: this has a significant effect both when validating a list of recent files (multiply each inaccessible file by 2-4 seconds) and when opening the standard JFileChooser(), which can take 1-2 minutes with a few disconnected drives.

Questions:

  1. Is this a known problem?
  2. Is there a workaround?

Context: Java 11 or 17 on Windows 10 with an existing "C:" drive, a non-existent "R:" drive, and a network drive "S:" that is not accessible.

Sample code:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;

public class FileSystemExperiment {

   private static final String[] FILES = {
      System.getProperty("user.home"), // home dir exists
      "/no/such/file",                 // missing file on current drive
      "C:\\no\\such\\file",            // missing file on existing drive
      "R:\\",                          // an unknown drive
      "S:\\",                          // a known but inaccessible drive
   };

   private static long started;

   private static void exists(String... pathStrings) {
      for (String pathString : pathStrings) {
         File file = new File(pathString);
         Path path = file.toPath();
         System.out.printf("%nFile '%s' ...%n", file);

         started = System.currentTimeMillis();
         time("file.exists()", file.exists());
         time("file.lastModified()>0", file.lastModified() > 0);
         time("Files.exists(path)", Files.exists(path));
         time("Files.readAttributes(path)", readAttributes(path) != null);
      }
   }

   private static void time(String msg, boolean exists) {
      System.out.printf("%-30s | %-5s | time=%s ms%n", msg, exists, System.currentTimeMillis() - started);
      started = System.currentTimeMillis();
   }

   private static BasicFileAttributes readAttributes(Path path) {
      try {
         return Files.readAttributes(path, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
      }
      catch (IOException e) {
         return null;
      }
   }

   public static void main(String[] args) {
      exists(FILES);
   }
}

Output:

File 'C:\Users\p2r' ...
file.exists()                  | true  | time=1 ms
file.lastModified()>=0         | true  | time=0 ms
Files.exists(path)             | true  | time=2 ms
Files.readAttributes(path)     | true  | time=0 ms

File '\no\such\file' ...
file.exists()                  | false | time=0 ms
file.lastModified()>=0         | false | time=0 ms
Files.exists(path)             | false | time=1 ms
Files.readAttributes(path)     | false | time=0 ms

File 'C:\no\such\file' ...
file.exists()                  | false | time=0 ms
file.lastModified()>=0         | false | time=0 ms
Files.exists(path)             | false | time=1 ms
Files.readAttributes(path)     | false | time=1 ms

File 'R:\' ...
file.exists()                  | false | time=0 ms
file.lastModified()>=0         | false | time=0 ms
Files.exists(path)             | false | time=0 ms
Files.readAttributes(path)     | false | time=0 ms

File 'S:\' ...
file.exists()                  | false | time=7802 ms
file.lastModified()>=0         | false | time=10067 ms
Files.exists(path)             | false | time=7171 ms
Files.readAttributes(path)     | false | time=10047 ms
p2r
  • 69
  • 6
  • What do you mean by "a known problem"? If you try to access something on a not accessible drive then Windows tries to access that drive for you and that takes time. Not much you (or Java) can do about it. – Thomas Kläger Sep 22 '22 at 08:49
  • 1
    @ThomasKläger I understand that it takes some time, but I cannot understand why it takes that much time. One effect is that it takes 1-2 minutes to instantiate a standard JFileChooser(new JFileChooser()) . Another is that my application gets degraded when I loop over recent files and filter out invalid entries; with a history of 20 files where 10 reside on an inaccessible network disc, it takes about 20 seconds to validate the list. By "known problem" I wonder if this is reported as a bug somewhere. I have a hard time believing I am the only one finding this acceptable? – p2r Sep 22 '22 at 09:03
  • 1
    I have observed this in the past too. What I noticed is that it is a common problem with `JFileChooser` in combination with network drives. Essentially as @ThomasKläger mentioned Windows will check for network drives and this may take longer amounts of time. Java is not responsible for this delay. Try accessing such a drive in the Windows explorer - you should notice that it takes similar amounts of time. – Koenigsberg Sep 22 '22 at 09:16
  • 1
    @Koenigsberg yes, the problem is that it is acceptable when navigating the explorer to such a drive, but not when just instantiating the JFileChooser with no intention of navigating to those drives. I would accept the delay when navigating to such a drive (just as in the Windows explorer), but not when just instantiating the chooser. – p2r Sep 22 '22 at 09:22
  • @Koenigsberg I still cannot understand why it takes that long for Windows to realize that the drive is unavailable ;-/ but I guess the real question is how to work around this - especially in the JFileChooser. – p2r Sep 22 '22 at 09:26
  • @p2r I agree. We never found a solution to this problem and as far as I am concerned it is still open. You are probably seeing the same behavior as is described [here](https://stackoverflow.com/questions/12293092/how-can-i-make-jfilechooser-behave-properly-with-disconnected-network-drives). – Koenigsberg Sep 22 '22 at 09:28
  • @Koenigsberg yes! Thanks for the link, I couldn't find it before. – p2r Sep 22 '22 at 09:29
  • @p2r I suppose one possible solution to try is to see if drives are being cached. This implies creating a `JFileChooser` whenever load times are acceptable, *e.g.* when starting the application. However even then these delays are very large and unnecessary if the user never intends to access a network drive. Sadly the delay is controlled by the OS, so if anything can be done about this it would have to happen on the user's machine - which is bad. – Koenigsberg Sep 22 '22 at 09:33
  • @Koenigsberg agreed. It took me quite some time to figure out that the JFileChooser was causing the GUI to hang, and then yet some more time to figure out that the cause was disconnected drives, hence I went looking for a solution or a viable workaround; the times when you encounter a unique and unresolved problem are rare these days ... ;-) I will update my post with these links. Thanks again! – p2r Sep 22 '22 at 09:38

1 Answers1

1

Q1: Yes, this is a known problem, reported in 1999 but still not resolved. See https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4260746:

Situation: You have connections to other computers in Win NT and these computers are not online. You start a JAVA-application (jdk1.2.1) with a JFileChooser. The dialog tries to "see" all the connected computers. Because some other computers are not avaiable, the filechooser-dialog appears after reaching the timeout. It may take 60 to 120 seconds until the dialog appears.

WORK AROUND: Close the network-connections to the computers that are not avaiable. The dialog appears just after invoking.

EVALUATION: This is an important performance problem and should be addressed in a not too distant release.

Q2: No, there are no feasible workarounds.

See How can I make JFileChooser behave properly with disconnected network drives?

It seems like it would be a common problem, but I can't find a way to work around it. Possible options I've seen are:

  • use JFileDialog; replace FileSystemView with one where getRoots() returns a fixed list of drives
  • switch to a WAIT cursor before trying to open JFileChooser(s)
  • create a JFileChooser at startup and keep it around forever
  • try using xfiledialog

In addition to the options mentioned above:

  • Check and cache availability of all drives using a background thread with a timeout
  • Wrap all calls to file operations (e.g. file.exists()) in methods that check the availability of the drive before delegating the call to the file
  • Instantiate the JFileChooser with a custom FileSystemView that wraps the default FileSystemView (FileSystemView.getFileSystemView()) and checks the drive status before delegating calls to the wrapped view
p2r
  • 69
  • 6