001package com.dpillay.tools.tail4j.characters; 002 003import java.io.BufferedInputStream; 004import java.io.BufferedReader; 005import java.io.File; 006import java.io.FileInputStream; 007import java.io.FileReader; 008import java.io.InputStream; 009 010import com.dpillay.tools.tail4j.configuration.TailConfiguration; 011import com.dpillay.tools.tail4j.core.TailListener; 012import com.dpillay.tools.tail4j.core.TailedReader; 013import com.dpillay.tools.tail4j.exception.ApplicationException; 014import com.dpillay.tools.tail4j.exception.ErrorCode; 015import com.dpillay.tools.tail4j.model.TailEvent; 016 017/** 018 * Implements a tailed file reader for string based contents 019 * 020 * @author dpillay 021 */ 022public class StringTailedFileReader implements TailedReader<String, File> { 023 private static char newLine = System.getProperty("line.separator") 024 .charAt(0); 025 026 private File file = null; 027 private TailListener<String> listener = null; 028 private TailConfiguration configuration = null; 029 030 public StringTailedFileReader(TailConfiguration tc, File file, 031 TailListener<String> listener) { 032 super(); 033 this.file = file; 034 this.listener = listener; 035 this.configuration = tc; 036 } 037 038 public StringTailedFileReader() { 039 } 040 041 @Override 042 public File getSource() { 043 return file; 044 } 045 046 @Override 047 public TailListener<String> getListener() { 048 return listener; 049 } 050 051 @Override 052 public void setSource(File file) { 053 this.file = file; 054 } 055 056 @Override 057 public void setListener(TailListener<String> listener) { 058 this.listener = listener; 059 } 060 061 public TailConfiguration getConfiguration() { 062 return configuration; 063 } 064 065 public void setConfiguration(TailConfiguration configuration) { 066 this.configuration = configuration; 067 } 068 069 @Override 070 public String call() throws Exception { 071 try { 072 BufferedReader br = new BufferedReader(new FileReader(file)); 073 long showLineCount = this.configuration.getShowLines(); 074 if (showLineCount > 0) { 075 FileInfo fileInfo = this 076 .getSkipLinesLength(file, showLineCount); 077 long skipLinesLength = fileInfo.getFileSkipLength(); 078 br.skip(skipLinesLength); 079 } else { 080 if (this.configuration.isForce()) { 081 br.skip(file.length()); 082 } else { 083 // showing 10 lines by default 084 showLineCount = 10; 085 FileInfo fileInfo = this.getSkipLinesLength(file, 086 showLineCount); 087 long skipLinesLength = fileInfo.getFileSkipLength(); 088 br.skip(skipLinesLength); 089 } 090 } 091 while (this.configuration.isForce() || showLineCount-- > 0) { 092 String line = br.readLine(); 093 if (line == null) { 094 if (!this.configuration.isForce()) 095 break; 096 Thread.sleep(200); 097 continue; 098 } 099 TailEvent<String> event = TailEvent.generateEvent(line, line 100 .length()); 101 this.listener.onTail(event); 102 } 103 } catch (Throwable t) { 104 throw new ApplicationException(t, ErrorCode.DEFAULT_ERROR, 105 "Could not finish tailing file"); 106 } 107 return null; 108 } 109 110 private FileInfo getSkipLinesLength(File file, long showLineCount) { 111 InputStream is = null; 112 long count = 0; 113 try { 114 is = new BufferedInputStream(new FileInputStream(file)); 115 long[] lineChars = new long[(int) showLineCount + 1]; 116 byte[] c = new byte[1024]; 117 int index = 0; 118 int readChars = 0; 119 long totalCharsRead = 0; 120 while ((readChars = is.read(c)) != -1) { 121 for (int i = 0; i < readChars; ++i) { 122 if (c[i] == newLine) { 123 ++count; 124 if (index == lineChars.length) 125 index = 0; 126 lineChars[index++] = totalCharsRead + i + 1; 127 } 128 } 129 totalCharsRead += readChars; 130 } 131 if (count >= showLineCount) { 132 return new FileInfo(count, 133 (index == lineChars.length) ? lineChars[0] 134 : lineChars[index]); 135 } 136 } catch (Exception e) { 137 } 138 return new FileInfo(count, 0); 139 } 140 141 private static class FileInfo { 142 long fileLineCount = 0; 143 long fileSkipLength = 0; 144 145 public FileInfo(long fileLineCount, long fileSkipLength) { 146 super(); 147 this.fileLineCount = fileLineCount; 148 this.fileSkipLength = fileSkipLength; 149 } 150 151 @SuppressWarnings("unused") 152 public long getFileLineCount() { 153 return fileLineCount; 154 } 155 156 public long getFileSkipLength() { 157 return fileSkipLength; 158 } 159 } 160}