org.apache.sling.commons.log.logback.internal.Tailer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sling.commons.log.logback.internal.Tailer.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.sling.commons.log.logback.internal;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;

class Tailer {
    static final int BUFFER_SIZE = 1024;
    private final int numOfLines;
    private final TailerListener listener;
    private final byte[] buffer = new byte[BUFFER_SIZE];

    public Tailer(PrintWriter printWriter, int numOfLines) {
        this(new PrinterListener(printWriter), numOfLines);
    }

    public Tailer(TailerListener listener, int numOfLines) {
        this.listener = listener;
        this.numOfLines = numOfLines;
    }

    interface TailerListener {
        /**
         * Handles a line from a Tailer.
         *
         * @param line the line.
         */
        void handle(String line);
    }

    public void tail(File file) throws IOException {
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(file, "r");
            long startPos = getTailStartPos(raf, numOfLines);
            readLines(raf, startPos);
        } finally {
            try {
                if (raf != null) {
                    raf.close();
                }
            } catch (IOException ignore) {

            }
        }
    }

    /**
     * Returns the starting position of UNIX "tail -n".
     */
    private long getTailStartPos(RandomAccessFile file, int n) throws IOException {
        int newlineCount = 0;
        long length = file.length();
        long pos = length - BUFFER_SIZE;
        int buffLength = BUFFER_SIZE;

        if (pos < 0) {
            pos = 0;
            buffLength = (int) length;
        }

        while (true) {
            file.seek(pos);
            file.readFully(buffer, 0, buffLength);

            for (int i = buffLength - 1; i >= 0; i--) {
                if ((char) buffer[i] == '\n') {
                    newlineCount++;

                    if (newlineCount >= n) {
                        pos += (i + 1);
                        return pos;
                    }
                }
            }

            if (pos == 0) {
                break;
            }

            if (pos - BUFFER_SIZE < 0) {
                buffLength = (int) pos;
                pos = 0;
            } else {
                pos -= BUFFER_SIZE;
            }
        }

        return pos;
    }

    /**
     * Read new lines. Code below is taken from org.apache.commons.io.input.Tailer
     *
     * @throws java.io.IOException if an I/O error occurs.
     * @param startPos position in file from where to start reading
     */
    private void readLines(RandomAccessFile file, long startPos) throws IOException {
        StringBuilder sb = new StringBuilder();

        file.seek(startPos);
        int num;
        boolean seenCR = false;
        while (((num = file.read(buffer)) != -1)) {
            for (int i = 0; i < num; i++) {
                byte ch = buffer[i];
                switch (ch) {
                case '\n':
                    seenCR = false; // swallow CR before LF
                    listener.handle(sb.toString());
                    sb.setLength(0);
                    break;
                case '\r':
                    if (seenCR) {
                        sb.append('\r');
                    }
                    seenCR = true;
                    break;
                default:
                    if (seenCR) {
                        seenCR = false; // swallow final CR
                        listener.handle(sb.toString());
                        sb.setLength(0);
                    }
                    sb.append((char) ch); // add character, not its ascii value
                }
            }
        }

        //Drain the left over part
        if (sb.length() != 0) {
            listener.handle(sb.toString());
        }
    }

    private static class PrinterListener implements Tailer.TailerListener {
        private final PrintWriter pw;

        public PrinterListener(PrintWriter pw) {
            this.pw = pw;
        }

        @Override
        public void handle(String line) {
            pw.println(line);
        }
    }

}