5

According to this document: http://technet.microsoft.com/en-us/library/ff686200%28v=ws.10%29.aspx File.exists() is not accurate on a smb2 network share. I am not able to change any register settings, so I want to deal with it. According to the document there is an API to get the notifications from the file system. I assumed that the WatchService is the Java implementation of this API. Am I correct?

I started with the WatchDir example from the jdk samples and stripped it a bit. I only need to know when a file is created and delete (I don't care about file modifications). For testing I have added new File.exists() when a new event has been triggered. I also start a separated Thread which test the file existence also. When I don't start this separated thread the file exists returns true immediately. When the extra thread is started it is not accurate any more. I need a more accurate file.exists check in the whole application and all running threads.

For testing I have used 2 Windows 7 pc's (running java 7) (with smb2 enabled which is default). The working directory must be on the remote pc and the file test.txt must be created (or copied from another folder) on the remote pc (not using the network drive, but on the pc itself).

Here is my test code:

import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.File;
import java.io.IOException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class WatchDir
{
  private final WatchService _watcher;
  private final String _dir;

  public WatchDir( String dir ) throws IOException
  {
    _dir = dir;
    _watcher = FileSystems.getDefault().newWatchService();
    Paths.get( dir ).register( _watcher, ENTRY_CREATE, ENTRY_DELETE );
    System.out.println( "watch registered for dir: " + dir );
  }

  public void run()
  {
    try
    {
      while ( true )
      {
        WatchKey key = _watcher.take();

        for ( WatchEvent<?> event : key.pollEvents() )
        {
          WatchEvent.Kind<?> kind = event.kind();

          if ( kind == OVERFLOW )
            continue;

          @SuppressWarnings( "unchecked" )
          WatchEvent<Path> ev = (WatchEvent<Path>)event;
          Path fileName = ev.context();

          System.out.println( "WatchDir event: " + kind.name() + ": " + fileName );

          if ( kind == ENTRY_CREATE )
          {
            String realPath = _dir + fileName;
            System.out.println( "WatchDir: " + realPath + " exists == " + new File( realPath ).exists() );
          }
        }
        key.reset();
      }
    }
    catch ( ClosedWatchServiceException x )
    {
      return;
    }
    catch ( InterruptedException ex )
    {
      return;
    }
  }

  public static void main( String[] args )
  {
    Thread t = new Thread( new Runnable()
    {
      @Override
      public void run()
      {
        try
        {
          while ( true )
          {
            String filename = "subdir\\test.txt";
            boolean fileExists = new File( filename ).exists();
            System.err.println( "FileExistsThread: " + filename + " == " + fileExists );
            Thread.sleep( 300 );
          }
        }
        catch ( InterruptedException e )
        {
          e.printStackTrace();
          return;
        }
      }
    } );
    t.start();

    try
    {
      new WatchDir( "subdir\\" ).run();
    }
    catch ( IOException e )
    {
      e.printStackTrace();
    }
  }
}

The output for my test case is this:

 1. FileExistsThread: subdir\test.txt == false  
 2. watch registered for dir: subdir\  
 3. FileExistsThread: subdir\test.txt == false  
 4. FileExistsThread: subdir\test.txt == false  
 5. FileExistsThread: subdir\test.txt == false  
 6. FileExistsThread: subdir\test.txt == false  
 7. FileExistsThread: subdir\test.txt == false  
 8. FileExistsThread: subdir\test.txt == false  
 9. WatchDir event: ENTRY_CREATE: test.txt  
10. WatchDir: subdir\test.txt exists == false  
11. FileExistsThread: subdir\test.txt == false  
12. FileExistsThread: subdir\test.txt == false  
13. FileExistsThread: subdir\test.txt == false  
14. FileExistsThread: subdir\test.txt == false  
15. FileExistsThread: subdir\test.txt == false  
16. FileExistsThread: subdir\test.txt == false  
17. FileExistsThread: subdir\test.txt == false  
18. FileExistsThread: subdir\test.txt == false  
19. FileExistsThread: subdir\test.txt == false  
20. FileExistsThread: subdir\test.txt == true  
21. FileExistsThread: subdir\test.txt == true  
22. FileExistsThread: subdir\test.txt == true  
23. FileExistsThread: subdir\test.txt == true  

As you can see the file test.txt is created on line 9. The watchService have notified it. But in real the application cannot use it: The FileExistsThread have seen it on line 20, (at least 10 x 300 ms later).

Any idea's?

[edit]

I also tried this :

 public synchronized static boolean fileExists( final String fileName )
 {
   File f = new File( fileName );
   String fullFileName = f.getAbsolutePath();
   final String shortfileName = f.getName();

   File dir = new File( fullFileName ).getParentFile();
   if ( dir == null )
     return false;

   String[] list = dir.list( new FilenameFilter()
   {
     @Override
     public boolean accept( File dir, String name )
     {
       return name.equalsIgnoreCase( shortfileName );
     }
   } );
   if ( list == null )
     return false;

   return list.length == 1;
 }

This method returns true when I expected. But I still cannot use the file after it. I actually want to open a new FileInputStream( file ). This throws a FileNotFoundExeption, while this fileExists() method returns true. Now I wait untill new File().exists() returns true before opening the FileInputStream. This works.

Thanks

Rob
  • 26,989
  • 16
  • 82
  • 98
Olaf
  • 146
  • 8
  • Have you tried replacing `new File(filename).exists()` with `Files.exists(Paths.get(filename))`? – VGR Jan 20 '15 at 15:50
  • Hi VGR, Yes have tried that. I also tried to find it in a directory list. I have paste that code in the original post. – Olaf Jan 22 '15 at 15:47

0 Answers0