1 package org.apache.commons.io.input;
2
3
4
5
6 import java.io.ByteArrayOutputStream;
7 import java.io.File;
8 import java.io.FileNotFoundException;
9 import java.io.IOException;
10 import java.io.RandomAccessFile;
11 import java.nio.charset.Charset;
12 import java.util.concurrent.atomic.AtomicBoolean;
13
14 import org.apache.commons.io.FileUtils;
15 import org.apache.commons.io.IOUtils;
16
17 class TailerRun implements Runnable {
18
19 private static final String RAF_MODE = "r";
20
21
22
23 private final Charset cset;
24
25
26
27 private final boolean end;
28
29
30
31
32 private final File file;
33
34 private final AtomicBoolean finished = new AtomicBoolean(false);
35
36
37
38
39 private final byte inbuf[];
40
41
42
43
44 private long last = 0;
45
46
47
48
49 private final TailerListener listener;
50
51
52
53
54 private long position = 0;
55 private RandomAccessFile reader = null;
56
57
58
59
60 private final boolean reOpen;
61
62 private final AtomicBoolean startedOnce = new AtomicBoolean(false);
63
64 public TailerRun(final File file, final Charset cset, final TailerListener listener, final boolean end,
65 final boolean reOpen, final int bufSize) {
66 this.file = file;
67 this.cset = cset;
68 this.end = end;
69 this.reOpen = reOpen;
70 this.inbuf = new byte[bufSize];
71 this.listener = listener;
72 }
73
74 public void cleanup() {
75 IOUtils.closeQuietly(this.reader);
76 this.reader = null;
77 this.finished.set(true);
78 }
79
80 public boolean hasFinished() {
81 return this.finished.get();
82 }
83
84 public boolean hasStartedOnce() {
85 return this.startedOnce.get();
86 }
87
88
89
90
91
92
93
94
95
96
97 private long readLines(final RandomAccessFile reader) throws IOException {
98 final ByteArrayOutputStream lineBuf = new ByteArrayOutputStream(64);
99 long pos = reader.getFilePointer();
100 long rePos = pos;
101 int num;
102 boolean seenCR = false;
103
104 while ((num = reader.read(this.inbuf)) != -1) {
105 for (int i = 0; i < num; i++) {
106 final byte ch = this.inbuf[i];
107 switch (ch) {
108 case '\n':
109 seenCR = false;
110 this.listener.handle(new String(lineBuf.toByteArray(), this.cset));
111 lineBuf.reset();
112 rePos = pos + i + 1;
113 break;
114 case '\r':
115 if (seenCR) {
116 lineBuf.write('\r');
117 }
118 seenCR = true;
119 break;
120 default:
121 if (seenCR) {
122 seenCR = false;
123 this.listener.handle(new String(lineBuf.toByteArray(), this.cset));
124 lineBuf.reset();
125 rePos = pos + i + 1;
126 }
127 lineBuf.write(ch);
128 }
129 }
130 pos = reader.getFilePointer();
131 }
132 IOUtils.closeQuietly(lineBuf);
133 reader.seek(rePos);
134 return rePos;
135 }
136
137 @Override
138 public void run() {
139 try {
140 this.startedOnce.set(true);
141 this.listener.begin();
142
143 if (this.reader == null) {
144 try {
145 this.reader = new RandomAccessFile(this.file, TailerRun.RAF_MODE);
146 } catch (final FileNotFoundException e) {
147 this.listener.fileNotFound();
148 return;
149 }
150
151 this.position = this.end ? this.file.length() : 0;
152 this.last = this.file.lastModified();
153 this.reader.seek(this.position);
154 }
155 final boolean newer = FileUtils.isFileNewer(this.file, this.last);
156
157
158
159
160
161 final long length = this.file.length();
162 if (length < this.position) {
163
164 this.listener.fileRotated();
165
166 try {
167
168
169 final RandomAccessFile save = this.reader;
170 this.reader = new RandomAccessFile(this.file, TailerRun.RAF_MODE);
171
172
173
174 try {
175 this.readLines(save);
176 } catch (final IOException ioe) {
177 this.listener.handle(ioe);
178 }
179 this.position = 0;
180
181
182 IOUtils.closeQuietly(save);
183 } catch (final FileNotFoundException e) {
184
185
186 this.listener.fileNotFound();
187 }
188 } else {
189
190
191 if (length > this.position) {
192
193 this.position = this.readLines(this.reader);
194 this.last = this.file.lastModified();
195 } else if (newer) {
196
197
198
199
200
201 this.position = 0;
202 this.reader.seek(this.position);
203
204
205 this.position = this.readLines(this.reader);
206 this.last = this.file.lastModified();
207 }
208 }
209 if (this.reOpen) {
210 IOUtils.closeQuietly(this.reader);
211 this.reader = new RandomAccessFile(this.file, TailerRun.RAF_MODE);
212 this.reader.seek(this.position);
213 }
214 } catch (final Exception e) {
215 this.listener.handle(e);
216 } finally {
217 this.listener.commit();
218 }
219 }
220
221 }