Java 7 WatchService - The process cannot access the file because it is being used by another process

jfajunior picture jfajunior · Mar 20, 2013 · Viewed 9.3k times · Source

I followed the Watching a Directory for Changes Java7 nio2 tutorial to recursively monitor the entire contents of a directory using the code sample WatchDir.java.

The code looks like this:

// Get list of events for the watch key.
for (WatchEvent<?> event : key.pollEvents()) {
// This key is registered only for ENTRY_CREATE events, but an OVERFLOW event 
// can occur regardless if events are lost or discarded.
if (event.kind() == OVERFLOW) {
    continue;
}

// Context for directory entry event is the file name of entry.
@SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path fileName = ev.context();
Path fullPath = dir.resolve(fileName);

try {
    // Print out event.
    System.out.print("Processing file: " + fileName);

    processed = fileProcessor.processFile(fullPath);

    System.out.println("Processed = " + processed);

    if (processed) {
        // Print out event.
        System.out.println(" - Done!");
    }
} 
catch (FileNotFoundException e) {
    System.err.println("Error message: " + e.getMessage());
}
catch (IOException e) {
    System.err.println("Error processing file: " + fileName.toString());
    System.err.println("Error message: " + e.getMessage());
}

Ok, so the problem (where I am sure doing something stupid) is here:

processed = fileProcessor.processFile(fullPath);

And what it does is something like this:

public synchronized boolean processFile(Path fullPath) throws IOException {
String line;
String[] tokens;
String fileName = fullPath.getFileName().toString();
String fullPathFileName = fullPath.toString();

// Create the file.
File sourceFile = new File(fullPath.toString());

// If the file does not exist, print out an error message and return.
if (sourceFile.exists() == false) {
    System.err.println("ERROR: " + fullPathFileName + ": No such file");
    return false;
}

// Check file extension.
if (!getFileExtension(fullPathFileName).equalsIgnoreCase("dat")) {
    System.out.println(" - Ignored.");
    return false;
}

// Process source file.
try (BufferedReader bReader = new BufferedReader(new FileReader(sourceFile))) {
    int type;

    // Process each line of the file.
    while (bReader.ready()) {

        // Get a single line.
        line = bReader.readLine();

        // Get line tokens.
        tokens = line.split(delimiter);

        // Get type.
        type = Integer.parseInt(tokens[0]);

        switch (type) {
        // Type 1 = Salesman.
        case 1:
            -> Call static method to process tokes.
            break;
        // Type 2 = Customer.
        case 2:
            -> Call static method to process tokes.
            break;
        // Type 3 = Sales.
        case 3:
            -> Call static method to process tokes.
            break;
        // Other types are unknown!
        default:
            System.err.println("Unknown type: " + type);
            break;
        }
    }

    PrintStream ps = null;
    try {

        // Write output file.
        // Doesn't matter. 

    } 
    finally {               
        if (ps != null) {
            ps.close();
        }
    }
    return true;
}
}

The first time I handle an event, it all works fine! Even if there is more than 1 file to process. But in the consecutive times, I get this error message:

The process cannot access the file because it is being used by another process

What am I doing wrong here? What can I do to process the consecutive files with sucess?

Two important notes that I forgot to mention:

  1. I'm using Windows 7.
  2. When I run the application in debug mode, it works.

EDIT: If I add a sleep before I try to use the file, it works:

Thread.sleep(500);

// Process source file.
try (BufferedReader bReader = new BufferedReader(new FileReader(sourceFile))) {

So, is it possible to be Windows that is not unlocking the files in time? How can I fix that (in a proper way)?

Answer

jfajunior picture jfajunior · Mar 20, 2013

Ok, I found a solution. I don't know if it is the best way to do this, but it works. Unfortunately, file.canRead() and file.canWrite() both return true, even if the file still locked by Windows. So I discovered that if I try to "rename" it with the same name, I know if Windows is working on it or not. So this is what I did:

    while(!sourceFile.renameTo(sourceFile)) {
        // Cannot read from file, windows still working on it.
        Thread.sleep(10);
    }