001/*
002 * (C) Copyright 2011-2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Julien Carsique
018 *
019 */
020
021package org.nuxeo.launcher.gui.logs;
022
023import java.io.BufferedReader;
024import java.io.File;
025import java.io.FileReader;
026import java.io.IOException;
027import java.util.Observable;
028
029import org.apache.commons.io.IOUtils;
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032
033/**
034 * @author jcarsique
035 * @since 5.4.2
036 */
037public class LogsSource extends Observable implements Runnable {
038    static final Log log = LogFactory.getLog(LogsSource.class);
039
040    private static final long WAIT_FOR_FILE_EXISTS = 2000;
041
042    private static final long WAIT_FOR_READING_CONTENT = 1000;
043
044    private File logFile;
045
046    private boolean pause = true;
047
048    private long charsToSkip = 0;
049
050    /**
051     * @param logFile Log file to manage
052     */
053    public LogsSource(File logFile) {
054        this.logFile = logFile;
055    }
056
057    @Override
058    public void run() {
059        BufferedReader in = null;
060        try {
061            while (!logFile.exists()) {
062                Thread.sleep(WAIT_FOR_FILE_EXISTS);
063            }
064            in = new BufferedReader(new FileReader(logFile));
065            // Avoid reading and formating chars which won't be displayed
066            if (charsToSkip > 0) {
067                in.skip(charsToSkip);
068            }
069            // marker for detecting log rotate
070            long lastModified = logFile.lastModified();
071            while (true) {
072                if (pause) {
073                    synchronized (this) {
074                        wait();
075                    }
076                }
077                String line = in.readLine();
078                if (line != null) {
079                    lastModified = logFile.lastModified();
080                    setChanged();
081                    notifyObservers(line);
082                } else {
083                    if (logFile.lastModified() > lastModified) {
084                        log.debug("File rotation detected");
085                        IOUtils.closeQuietly(in);
086                        in = new BufferedReader(new FileReader(logFile));
087                    } else {
088                        synchronized (this) {
089                            wait(WAIT_FOR_READING_CONTENT);
090                        }
091                    }
092                }
093            }
094        } catch (InterruptedException e) {
095            log.debug(e);
096        } catch (IOException e) {
097            log.error(e);
098        } finally {
099            IOUtils.closeQuietly(in);
100        }
101    }
102
103    /**
104     * Ask thread to pause until {@link #resume()}
105     */
106    public synchronized void pause() {
107        pause = true;
108    }
109
110    /**
111     * Resume thread after call to {@link #pause()}
112     */
113    public void resume() {
114        pause = false;
115        synchronized (this) {
116            notify();
117        }
118    }
119
120    /**
121     * @param length
122     */
123    public void skip(long length) {
124        charsToSkip = length;
125    }
126
127}