javabufferedreaderread-write

BufferedReader reading empty lines instead of newly added lines


Im currently working on a application to manage a remote minecraft server and the first thing Im working on is observing the messages in the minecraft console. Im doing this by observing the latest.log textfile in which the minecraft server copies its output. The log messages are added each in a newline, so the basic workflow for monitoring is:

start -> read all existing lines of latest.log -> watch for file change notifications on latest.log -> 
read newly added line(s) -> wait for next file change notification

Ive implemented the following class to do this:

public class McServerService {
    
    
    
    private String directory;
    
    
    private List<String> currentLog;
        
    private Thread logObserverThread;
    private PropertyChangeSupport pcsupport;
    
    
    public McServerService (String directory) {
        this.currentLog = new ArrayList<String>();
        this.directory = directory;
        this.pcsupport = new PropertyChangeSupport(this);
    }
    
    public void startWatching ()  {
        this.logObserverThread = new Thread(new LogObserverThreadImpl(this.directory));
        this.logObserverThread.start();
    }
    
    public void addNewLogLine (String newLogLine) {
        this.pcsupport.firePropertyChange("currentLog", this.currentLog, newLogLine);
        this.currentLog.add(newLogLine);
        System.out.println("addNewLogLine: " + newLogLine);
        
    }
    
    public void addPropertyChangeListener (PropertyChangeListener pcl) {
        this.pcsupport.addPropertyChangeListener(pcl);
    }
    
    public void removePropertyChangeListener (PropertyChangeListener pcl) {
        this.pcsupport.removePropertyChangeListener(pcl);
    }
    
    
        
    private class LogObserverThreadImpl implements Runnable {
        
        BufferedReader br;
        WatchService watchService;
        
        private LogObserverThreadImpl (String directory) {
            try {
                this.br = new BufferedReader(new java.io.FileReader(directory + "\\" + "latest.log"));
                String nextLine = this.br.readLine();
                while (nextLine != null) {
                    McServerService.this.currentLog.add(nextLine);
                    System.out.println("init: " + nextLine);
                    this.br.mark(2048);
                    nextLine = this.br.readLine();
                    System.out.println("init: " + nextLine);
                }
                this.br.reset();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            Path path = Paths.get(directory);
            try {
                System.out.println("entered try");
                this.watchService = FileSystems.getDefault().newWatchService();
                path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY);
                WatchKey key;
                while ((key = this.watchService.take()) != null) {
                    for (WatchEvent<?> event : key.pollEvents()) {
                        if (event.context().toString().equals("latest.log")) {
                            String line = this.br.readLine();
                            /*
                             * if (line.equalsIgnoreCase("")) { line = this.br.readLine(); }
                             */
                            McServerService.this.addNewLogLine(line);
                            System.out.println("thread: " + line);
                        }
                    }
                    key.reset();
                }
                System.out.println("after while");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }
    
    

    
    
}

The existing latest.log textfile is read as exspected with all its lines and then ends on null, but when adding the two lines

gadhsjlhgadsjlkh
jlhkadshljhads

and saving the file after each line, the output looks like this:

init: null //"last" line of existing file
entered try
//now adding the two lines
pclimlp: 
addNewLogLine: 
thread: 
pclimlp: gadhsjlhgadsjlkh
addNewLogLine: gadhsjlhgadsjlkh
thread: gadhsjlhgadsjlkh

The problem is solved by uncommenting the equals check for "" in:

while ((key = this.watchService.take()) != null) {
                    for (WatchEvent<?> event : key.pollEvents()) {
                        if (event.context().toString().equals("latest.log")) {
                            String line = this.br.readLine();
                            /*
                             * if (line.equalsIgnoreCase("")) { line = this.br.readLine(); }
                             */
                            McServerService.this.addNewLogLine(line);
                            System.out.println("thread: " + line);
                        }
                    }
                    key.reset();
                }

But why is the BufferedReder reading empty lines there in the first place? Id understand null if the BufferedReader isnt updated yet, but why empty lines? Also if I manually save the file again (without adding more lines or anything), the BufferedReader will read the line it hasnt read before fine and is not skipping it. Can someone explain what happens there to cause this behaviour?


Solution

  • BufferedReade.readLine() reads until either the next linefeed or the end of file.

    So when reading this file:

    [......]
    [21:34:31] [Server thread/INFO]: [removed] left the game
    

    BufferedReader gives the following output:

    [......]
    init: [21:34:31] [Server thread/INFO]: [removed] left the game
    init: null
    

    "init: " was only added for debug purposes. When appending

    xx
    ghasjdkhajs
    

    So the end of the file looks like this:

    [21:34:31] [Server thread/INFO]: [removed] left the gamexx
    ghasjdkhajs
    

    This leads to the following output:

    pclimlp: xx
    addNewLogLine: xx
    thread: xx
    

    So BufferedReader was at the end of the file which was directly after

    left the game
    

    So when adding

    xx\r\nghasjdkhajs
    

    the BufferedReader reads until the next linefeed. If there wasnt the "xx" added there would simply be nothing before the next linefeed, so the BufferedReader would return "" like in the question.