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