开发者

Java: opening and reading from a file without locking it

开发者 https://www.devze.com 2022-12-24 06:20 出处:网络
I need to be able to mimic \'tail -f\' with Java. I\'m trying to read a log file as it\'s being written by another process, but when I open the file to read it, it locks the file and the other process

I need to be able to mimic 'tail -f' with Java. I'm trying to read a log file as it's being written by another process, but when I open the file to read it, it locks the file and the other process can't write to it anymore. Any help would be greatly appreciated!

Here is the code that I'm using currently:

public void read(){
    Scanner fp = null;
    try{
        fp = new Scanner(new FileReader(this.filename));
        fp.useDelimiter("\n");
    }catch(java.io.FileNotFoundException e){
        System.out.println("java.io.FileNotFoundException e");
    }
    while(true){
        if(f开发者_JS百科p.hasNext()){
            this.parse(fp.next());
        }           
    }       
}


Rebuilding tail is tricky due to some special cases like file truncation and (intermediate) deletion. To open the file without locking use StandardOpenOption.READ with the new Java file API like so:

try (InputStream is = Files.newInputStream(path, StandardOpenOption.READ)) {
    InputStreamReader reader = new InputStreamReader(is, fileEncoding);
    BufferedReader lineReader = new BufferedReader(reader);
    // Process all lines.
    String line;
    while ((line = lineReader.readLine()) != null) {
        // Line content content is in variable line.
    }
}

For my attempt to create a tail in Java see:

  • Method examineFile(…) in https://github.com/AugustusKling/yield/blob/master/src/main/java/yield/input/file/FileMonitor.java
  • The above is used by https://github.com/AugustusKling/yield/blob/master/src/main/java/yield/input/file/FileInput.java to create a tail operation. The queue.feed(lineContent) passes line content for processing by listeners and would equal your this.parse(…).

Feel free to take inspiration from that code or simply copy the parts you require. Let me know if you find any issues that I'm not aware of.


Look at the FileChannel API here. For locking the file you can check here


java.io gives you a mandatory file lock and java.nio gives you an advisory file lock

If you want to read any file without any lock you can use below classes

import java.nio.channels.FileChannel;
import java.nio.file.Paths;

If you want to tail a file line by line use below code for the same

public void tail(String logPath){
    String logStr = null;
    FileChannel fc = null;
    try {
        fc = FileChannel.open(Paths.get(logPath), StandardOpenOption.READ);
        fc.position(fc.size());
    } catch (FileNotFoundException e1) {
        System.out.println("FileNotFoundException occurred in Thread : " + Thread.currentThread().getName());
        return;
    } catch (IOException e) {
        System.out.println("IOException occurred while opening FileChannel in Thread : " + Thread.currentThread().getName());
    }
    while (true) {
        try {
            logStr = readLine(fc);
            if (logStr != null) {
                System.out.println(logStr);
            } else {
                Thread.sleep(1000);
            }
        } catch (IOException|InterruptedException e) {
            System.out.println("Exception occurred in Thread : " + Thread.currentThread().getName());
            try {
                fc.close();
            } catch (IOException e1) {
            }
            break;
        }
    }
}

private String readLine(FileChannel fc) throws IOException {
    ByteBuffer buffers = ByteBuffer.allocate(128);
    // Standard size of a line assumed to be 128 bytes
    long lastPos = fc.position();
    if (fc.read(buffers) > 0) {
        byte[] data = buffers.array();
        boolean foundTmpTerminator = false;
        boolean foundTerminator = false;
        long endPosition = 0;
        for (byte nextByte : data) {
            endPosition++;
            switch (nextByte) {
            case -1:
                foundTerminator = true;
                break;
            case (byte) '\r':
                foundTmpTerminator = true;
                break;
            case (byte) '\n':
                foundTmpTerminator = true;
                break;
            default:
                if (foundTmpTerminator) {
                    endPosition--;
                    foundTerminator = true;
                }
            }
            if (foundTerminator) {
                break;
            }
        }
        fc.position(lastPos + endPosition);
        if (foundTerminator) {
            return new String(data, 0, (int) endPosition);
        } else {
            return new String(data, 0, (int) endPosition) + readLine(fc);
        }
    }
    return null;
}


Windows uses mandatory locking for files unless you specify the right share flags while you open. If you want to open a busy file, you need to Win32-API CreateFile a handle with the sharing flags FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE.

This is used inside the JDK in a few places to open files for reading attributes and stuff, but as far as I can see it is not exported/available to Java Class Library level. So you would need to find a native library to do that.

I think as a quick work around you can read process.getInputStream() from the command "cmd /D/C type file.lck"

0

精彩评论

暂无评论...
验证码 换一张
取 消