/* * Copyright 2003-2012 Yusuke Yamamoto * * Licensed 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 samurai.tail; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.List; public class LogWatcher extends Thread { private List<LogMonitor> logMonitors = new ArrayList<LogMonitor>(3); private boolean killed = false; private boolean debug = false; private List<File> pendingFiles = new ArrayList<File>(0); private List<File> newPendingFiles = null; private String encoding; public void setFiles(File[] files) { newPendingFiles = new ArrayList<File>(); for (int i = 0; i < files.length; i++) { newPendingFiles.add(files[i]); } } public void setFile(File file) { setFiles(new File[]{file}); } public LogWatcher() { this(System.getProperty("file.encoding")); } public LogWatcher(String encoding) { super.setName("LogWatcher Thread"); this.setDaemon(true); this.setPriority(Thread.MIN_PRIORITY); this.encoding = encoding; } public void setDebug(boolean debug) { this.debug = debug; } public void addLogMonitor(LogMonitor logMonitor) { logMonitors.add(logMonitor); } public synchronized void kill() { if (!killed) { this.killed = true; synchronized (logMonitors) { logMonitors.clear(); } try { this.wait(); } catch (InterruptedException ignore) { } } } public boolean killed() { return killed; } private long filePointer = 0; private boolean hasEnded = false; private boolean hasStarted = false; private File file = null; boolean updateDetected = false; ByteArrayOutputStream line = new ByteArrayOutputStream(128); RandomAccessFile raf; int waitCount = 0; public void run() { log("analyze(" + file + ")"); while (!killed) { checkUpdate(); } log("dying"); synchronized (this) { this.notify(); } } public void checkUpdate() { if (debug) log("loop"); if (null != newPendingFiles) { synchronized (this) { if (hasStarted && !hasEnded) { logEnded(); } hasStarted = false; waitCount = 0; pendingFiles = newPendingFiles; newPendingFiles = null; } } try { if (null != file) { if (file.exists()) { raf = new RandomAccessFile(file, "r"); raf.seek(filePointer); while (readLine(raf, line) && !killed) { if (!hasStarted) { logStarted(); } onLine(line.toByteArray()); line.reset(); } filePointer = raf.getFilePointer(); raf.close(); } } updateDetected = false; do { //read pending file if exists if (pendingFiles.size() != 0) { if (debug) log("found pending file"); file = pendingFiles.remove(0); filePointer = 0; line = new ByteArrayOutputStream(128); updateDetected = true; hasEnded = false; break; } else { if (1 == waitCount) { sleeping(); } //skip sleep when pending file exists try { // if (debug) // log("sleep"); Thread.sleep(50); } catch (InterruptedException ie) { } } if (null != file && file.exists()) { if (file.length() == filePointer) { if (hasStarted && !hasEnded) { if (debug) log("waitcount:" + waitCount); if (40 == waitCount++) { if (debug) { log("no new line detected for 1 second"); } logEnded(); waitCount = 0; } } } else { //file size increased or decreased updateDetected = true; if (file.length() > filePointer) { //size increased logContinued(); } else { //size decreased if (!hasEnded) { logEnded(); } filePointer = 0; logStarted(); } } } else { //file disappeared if (!hasEnded) { logEnded(); } filePointer = 0; } } while (!updateDetected && !killed && null == newPendingFiles); } catch (IOException ex) { onException(ex); } } public final boolean readLine(RandomAccessFile raf, ByteArrayOutputStream line) throws IOException { int c = -1; boolean eol = false; // long lastFilePointer; while (!eol) { // lastFilePointer = raf.getFilePointer(); c = raf.read(); switch (c) { case -1: return false; case '\n': eol = true; break; case '\r': eol = true; // lastFilePointer = raf.getFilePointer(); if ((raf.read()) != '\n') { raf.seek(raf.getFilePointer() - 1); } break; default: line.write(c); break; } } return eol; } // private int i; private synchronized void onLine(byte[] line) { if (null == newPendingFiles) { if (debug) log("onLine(" + line + ")"); for (LogMonitor monitor : logMonitors) { try { monitor.onLine(this.file, new String(line, encoding), this.filePointer); } catch (java.io.UnsupportedEncodingException uee) { uee.printStackTrace(); } catch (RuntimeException re) { re.printStackTrace(); } } } } private synchronized void logStarted() { if (null == newPendingFiles) { hasStarted = true; hasEnded = false; if (debug) log("logStarted()"); for (LogMonitor monitor : logMonitors) { try { monitor.logStarted(this.file, this.filePointer); } catch (RuntimeException re) { re.printStackTrace(); } } } } private synchronized void logEnded() { if (null == newPendingFiles) { hasEnded = true; if (debug) log("logEnded()"); for (LogMonitor monitor : logMonitors) { try { monitor.logEnded(this.file, this.filePointer); } catch (RuntimeException re) { re.printStackTrace(); } } } } private synchronized void logContinued() { if (null == newPendingFiles) { hasEnded = false; if (debug) log("logContinued()"); for (LogMonitor monitor : logMonitors) { try { monitor.logContinued(this.file, this.filePointer); } catch (RuntimeException re) { re.printStackTrace(); } } } } private synchronized void onException(IOException ioe) { if (null == newPendingFiles) { if (debug) log("onException()"); for (LogMonitor monitor : logMonitors) { try { monitor.onException(this.file, ioe); } catch (RuntimeException re) { re.printStackTrace(); } } } } private synchronized void sleeping() { if (!hasEnded && hasStarted) { for (LogMonitor monitor : logMonitors) { try { monitor.logWillEnd(this.file, this.filePointer); } catch (RuntimeException re) { re.printStackTrace(); } } } } private void log(String msg) { if (debug) { System.out.println("logWatcher:" + msg); } } }