106106 */
107107public class Tailer implements Runnable {
108108
109+ private static final String RAF_MODE = "r" ;
110+
111+ private static final int DEFAULT_BUFSIZE = 4096 ;
112+
113+ /**
114+ * Buffer on top of RandomAccessFile.
115+ */
116+ private byte inbuf [];
117+
109118 /**
110119 * The file which will be tailed.
111120 */
@@ -158,33 +167,61 @@ public Tailer(File file, TailerListener listener, long delay) {
158167 * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file.
159168 */
160169 public Tailer (File file , TailerListener listener , long delay , boolean end ) {
170+ this (file , listener , delay , end , DEFAULT_BUFSIZE );
171+ }
172+
173+ /**
174+ * Creates a Tailer for the given file, with a specified buffer size.
175+ * @param file the file to follow.
176+ * @param listener the TailerListener to use.
177+ * @param delay the delay between checks of the file for new content in milliseconds.
178+ * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file.
179+ * @param bufSize Buffer size
180+ */
181+ public Tailer (File file , TailerListener listener , long delay , boolean end , int bufSize ) {
161182
162183 this .file = file ;
163184 this .delay = delay ;
164185 this .end = end ;
186+
187+ this .inbuf = new byte [bufSize ];
165188
166189 // Save and prepare the listener
167190 this .listener = listener ;
168191 listener .init (this );
169192 }
170-
193+
171194 /**
172195 * Creates and starts a Tailer for the given file.
173196 *
174197 * @param file the file to follow.
175198 * @param listener the TailerListener to use.
176199 * @param delay the delay between checks of the file for new content in milliseconds.
177200 * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file.
201+ * @param buffer size.
178202 * @return The new tailer
179203 */
180- public static Tailer create (File file , TailerListener listener , long delay , boolean end ) {
181- Tailer tailer = new Tailer (file , listener , delay , end );
204+ public static Tailer create (File file , TailerListener listener , long delay , boolean end , int bufSize ) {
205+ Tailer tailer = new Tailer (file , listener , delay , end , bufSize );
182206 Thread thread = new Thread (tailer );
183207 thread .setDaemon (true );
184208 thread .start ();
185209 return tailer ;
186210 }
187211
212+ /**
213+ * Creates and starts a Tailer for the given file with default buffer size.
214+ *
215+ * @param file the file to follow.
216+ * @param listener the TailerListener to use.
217+ * @param delay the delay between checks of the file for new content in milliseconds.
218+ * @param end Set to true to tail from the end of the file, false to tail from the beginning of the file.
219+ * @return The new tailer
220+ */
221+ public static Tailer create (File file , TailerListener listener , long delay , boolean end ) {
222+ return create (file , listener , delay , end , DEFAULT_BUFSIZE );
223+ }
224+
188225 /**
189226 * Creates and starts a Tailer for the given file, starting at the beginning of the file
190227 *
@@ -238,7 +275,7 @@ public void run() {
238275 // Open the file
239276 while (run && reader == null ) {
240277 try {
241- reader = new RandomAccessFile (file , "r" );
278+ reader = new RandomAccessFile (file , RAF_MODE );
242279 } catch (FileNotFoundException e ) {
243280 listener .fileNotFound ();
244281 }
@@ -252,11 +289,10 @@ public void run() {
252289 // The current position in the file
253290 position = end ? file .length () : 0 ;
254291 last = System .currentTimeMillis ();
255- reader .seek (position );
292+ reader .seek (position );
256293 }
257294 }
258295
259-
260296 while (run ) {
261297
262298 // Check the file length to see if it was rotated
@@ -271,7 +307,7 @@ public void run() {
271307 try {
272308 // Ensure that the old file is closed iff we re-open it successfully
273309 RandomAccessFile save = reader ;
274- reader = new RandomAccessFile (file , "r" );
310+ reader = new RandomAccessFile (file , RAF_MODE );
275311 position = 0 ;
276312 // close old file explicitly rather than relying on GC picking up previous RAF
277313 IOUtils .closeQuietly (save );
@@ -293,9 +329,9 @@ public void run() {
293329
294330 } else if (FileUtils .isFileNewer (file , last )) {
295331
296- /* This can happen if the file is truncated or overwritten
297- * with the exact same length of information. In cases like
298- * this, the file position needs to be reset
332+ /*
333+ * This can happen if the file is truncated or overwritten with the exact same length of
334+ * information. In cases like this, the file position needs to be reset
299335 */
300336 position = 0 ;
301337 reader .seek (position ); // cannot be null here
@@ -335,31 +371,22 @@ public void stop() {
335371 * @throws java.io.IOException if an I/O error occurs.
336372 */
337373 private long readLines (RandomAccessFile reader ) throws IOException {
374+ StringBuilder sb = new StringBuilder ();
375+
338376 long pos = reader .getFilePointer ();
339- String line = readLine (reader );
340- while (line != null ) {
341- pos = reader .getFilePointer ();
342- listener .handle (line );
343- line = readLine (reader );
344- }
345- reader .seek (pos ); // Ensure we can re-read if necessary
346- return pos ;
347- }
377+ long rePos = pos ; // position to re-read
348378
349- /**
350- * Version of readline() that returns null on EOF rather than a partial line.
351- * @param reader the input file
352- * @return the line, or null if EOF reached before '\n' is seen.
353- * @throws IOException if an error occurs.
354- */
355- private String readLine (RandomAccessFile reader ) throws IOException {
356- StringBuffer sb = new StringBuffer ();
357- int ch ;
379+ int num ;
358380 boolean seenCR = false ;
359- while ((ch =reader .read ()) != -1 ) {
360- switch (ch ) {
381+ while (run && ((num = reader .read (inbuf )) != -1 )) {
382+ for (int i = 0 ; i < num ; i ++) {
383+ byte ch = inbuf [i ];
384+ switch (ch ) {
361385 case '\n' :
362- return sb .toString ();
386+ listener .handle (sb .toString ());
387+ sb = new StringBuilder ();
388+ rePos = pos + i + 1 ;
389+ break ;
363390 case '\r' :
364391 seenCR = true ;
365392 break ;
@@ -368,9 +395,15 @@ private String readLine(RandomAccessFile reader) throws IOException {
368395 sb .append ('\r' );
369396 seenCR = false ;
370397 }
371- sb .append ((char )ch ); // add character, not its ascii value
398+ sb .append ((char ) ch ); // add character, not its ascii value
399+ }
372400 }
401+
402+ pos = reader .getFilePointer ();
373403 }
374- return null ;
404+
405+ reader .seek (rePos ); // Ensure we can re-read if necessary
406+ return rePos ;
375407 }
408+
376409}
0 commit comments