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

4

8

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)?

Orangeman answered 20/3, 2013 at 17:52 Comment(0)
O
18

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);
    }
Orangeman answered 20/3, 2013 at 20:43 Comment(2)
I'm not sure relying on this is safe. It would be perfectly legal for Oracle to one day add a check to the renameTo() method where they exit early if the you are trying to rename to self. And then your workaround would no longer work.Spier
Peterh, you should never rely on a "work around" as a definitive answer to a problem, hence the name. ;) This solution was done when Java was still under control of Sun Microsystems. If one found a better answer nowadays, please share. This is the magical beauty of Stack Overflow.Orangeman
S
2

Solution given by Arundev worked for me.

I needed to add sleep time otherwise it wouldn't work for multiple requests and used to get the error "The process cannot access the file because it is being used by another process"

if (StandardWatchEventKinds.ENTRY_CREATE.equals(event.kind())) {
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

// now do your intended jobs ... 
Schiller answered 27/4, 2020 at 23:53 Comment(0)
E
1

I had the similar problem. But my file isn't being locked when it's being copied . If I had been trying to read it right after I had got created event that It was empty.

I think in your case there are no need tricks and you can simply handle error and if you get error than you can execute sleep and try again after that. But for me it's not right for me. I don't get error. So I tried your soloution and I understood that it isn't acceptable for me too because I don't have rights except reading. So I can suggest my 'trick'. I'm using class java.io.RandomAccessFile:

List<String> lines = null;

while(true){
    try( RandomAccessFile raf = new RandomAccessFile( fileFullPath.toFile() , "r" ) ){
        lines = Files.readAllLines( fileFullPath , Charset.forName(ENCODING) );
        break;
    }catch(FileNotFoundException ex){
        LOG.warn("File isn't yet completed: {}",ex.getMessage() );
        LOG.info( "Waiting {} for next attempt to read it" , SLEEP_INTERVAL_READING );
        try {
            Thread.sleep(SLEEP_INTERVAL_READING);
        } catch (InterruptedException e) {
            LOG.error("Waiting was interrupted",e);
        }
    }catch(Exception ex){
        LOG.error("Error in reading file ",ex);
        clear();
        return false;
    }
} 
Evelunn answered 22/4, 2016 at 15:22 Comment(0)
C
0

When event is of type ENTRY_MODIFY please allow the current thread to sleep for a few seconds. I cant exactly tell why this is happening but some time multiple ENTRY_MODIFY event is printing in my logs. Depend on your file size please set the sleep time.

if(kind ==  StandardWatchEventKinds.ENTRY_MODIFY){
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
//toDo - something
}
Controvert answered 22/2, 2018 at 10:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.