Author: ggregory
Date: Fri Jun  1 17:27:21 2012
New Revision: 1345279

URL: http://svn.apache.org/viewvc?rev=1345279&view=rev
Log:
[IO-332] Improve tailer's reading performance.

Modified:
    commons/proper/io/trunk/src/changes/changes.xml
    
commons/proper/io/trunk/src/main/java/org/apache/commons/io/input/Tailer.java

Modified: commons/proper/io/trunk/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/io/trunk/src/changes/changes.xml?rev=1345279&r1=1345278&r2=1345279&view=diff
==============================================================================
--- commons/proper/io/trunk/src/changes/changes.xml (original)
+++ commons/proper/io/trunk/src/changes/changes.xml Fri Jun  1 17:27:21 2012
@@ -47,6 +47,9 @@ The <action> type attribute can be add,u
   <body>
     <!-- The release date is the date RC is cut -->
     <release version="2.4" date="2012-TDB-TDB" description="">
+      <action issue="IO-332" dev="ggregory" type="fix" due-to="liangly">
+        Improve tailer's reading performance.
+      </action>            
       <action issue="IO-329" dev="ggregory" type="fix" due-to="tivv">
         FileUtils.writeLines uses unbuffered IO.
       </action>            

Modified: 
commons/proper/io/trunk/src/main/java/org/apache/commons/io/input/Tailer.java
URL: 
http://svn.apache.org/viewvc/commons/proper/io/trunk/src/main/java/org/apache/commons/io/input/Tailer.java?rev=1345279&r1=1345278&r2=1345279&view=diff
==============================================================================
--- 
commons/proper/io/trunk/src/main/java/org/apache/commons/io/input/Tailer.java 
(original)
+++ 
commons/proper/io/trunk/src/main/java/org/apache/commons/io/input/Tailer.java 
Fri Jun  1 17:27:21 2012
@@ -106,6 +106,15 @@ import org.apache.commons.io.IOUtils;
  */
 public class Tailer implements Runnable {
 
+    private static final String RAF_MODE = "r";
+
+    private static final int DEFAULT_BUFSIZE = 4096;
+  
+    /**
+     * Buffer on top of RandomAccessFile.
+     */
+    private byte inbuf[];
+    
     /**
      * The file which will be tailed.
      */
@@ -158,16 +167,30 @@ public class Tailer implements Runnable 
      * @param end Set to true to tail from the end of the file, false to tail 
from the beginning of the file.
      */
     public Tailer(File file, TailerListener listener, long delay, boolean end) 
{
+        this(file, listener, delay, end, DEFAULT_BUFSIZE);
+    }
+    
+    /**
+     * Creates a Tailer for the given file, with a specified buffer size.
+     * @param file the file to follow.
+     * @param listener the TailerListener to use.
+     * @param delay the delay between checks of the file for new content in 
milliseconds.
+     * @param end Set to true to tail from the end of the file, false to tail 
from the beginning of the file.
+     * @param bufSize Buffer size
+     */
+    public Tailer(File file, TailerListener listener, long delay, boolean end, 
int bufSize) {
 
         this.file = file;
         this.delay = delay;
         this.end = end;
+        
+        this.inbuf = new byte[bufSize];
 
         // Save and prepare the listener
         this.listener = listener;
         listener.init(this);
     }
-
+    
     /**
      * Creates and starts a Tailer for the given file.
      * 
@@ -175,10 +198,11 @@ public class Tailer implements Runnable 
      * @param listener the TailerListener to use.
      * @param delay the delay between checks of the file for new content in 
milliseconds.
      * @param end Set to true to tail from the end of the file, false to tail 
from the beginning of the file.
+     * @param buffer size.
      * @return The new tailer
      */
-    public static Tailer create(File file, TailerListener listener, long 
delay, boolean end) {
-        Tailer tailer = new Tailer(file, listener, delay, end);
+    public static Tailer create(File file, TailerListener listener, long 
delay, boolean end, int bufSize) {
+        Tailer tailer = new Tailer(file, listener, delay, end, bufSize);
         Thread thread = new Thread(tailer);
         thread.setDaemon(true);
         thread.start();
@@ -186,6 +210,19 @@ public class Tailer implements Runnable 
     }
 
     /**
+     * Creates and starts a Tailer for the given file with default buffer size.
+     * 
+     * @param file the file to follow.
+     * @param listener the TailerListener to use.
+     * @param delay the delay between checks of the file for new content in 
milliseconds.
+     * @param end Set to true to tail from the end of the file, false to tail 
from the beginning of the file.
+     * @return The new tailer
+     */
+    public static Tailer create(File file, TailerListener listener, long 
delay, boolean end) {
+        return create(file, listener, delay, end, DEFAULT_BUFSIZE);
+    }
+
+    /**
      * Creates and starts a Tailer for the given file, starting at the 
beginning of the file
      * 
      * @param file the file to follow.
@@ -238,7 +275,7 @@ public class Tailer implements Runnable 
             // Open the file
             while (run && reader == null) {
                 try {
-                    reader = new RandomAccessFile(file, "r");
+                    reader = new RandomAccessFile(file, RAF_MODE);
                 } catch (FileNotFoundException e) {
                     listener.fileNotFound();
                 }
@@ -252,11 +289,10 @@ public class Tailer implements Runnable 
                     // The current position in the file
                     position = end ? file.length() : 0;
                     last = System.currentTimeMillis();
-                    reader.seek(position);                    
+                    reader.seek(position);
                 }
             }
 
-
             while (run) {
 
                 // Check the file length to see if it was rotated
@@ -271,7 +307,7 @@ public class Tailer implements Runnable 
                     try {
                         // Ensure that the old file is closed iff we re-open 
it successfully
                         RandomAccessFile save = reader;
-                        reader = new RandomAccessFile(file, "r");
+                        reader = new RandomAccessFile(file, RAF_MODE);
                         position = 0;
                         // close old file explicitly rather than relying on GC 
picking up previous RAF
                         IOUtils.closeQuietly(save);
@@ -293,9 +329,9 @@ public class Tailer implements Runnable 
 
                     } else if (FileUtils.isFileNewer(file, last)) {
 
-                        /* This can happen if the file is truncated or 
overwritten
-                         * with the exact same length of information. In cases 
like
-                         * this, the file position needs to be reset
+                        /*
+                         * This can happen if the file is truncated or 
overwritten with the exact same length of
+                         * information. In cases like this, the file position 
needs to be reset
                          */
                         position = 0;
                         reader.seek(position); // cannot be null here
@@ -335,31 +371,22 @@ public class Tailer implements Runnable 
      * @throws java.io.IOException if an I/O error occurs.
      */
     private long readLines(RandomAccessFile reader) throws IOException {
+        StringBuilder sb = new StringBuilder();
+
         long pos = reader.getFilePointer();
-        String line = readLine(reader);
-        while (line != null) {
-            pos = reader.getFilePointer();
-            listener.handle(line);
-            line = readLine(reader);
-        }
-        reader.seek(pos); // Ensure we can re-read if necessary
-        return pos;
-    }
+        long rePos = pos; // position to re-read
 
-    /**
-     * Version of readline() that returns null on EOF rather than a partial 
line.
-     * @param reader the input file
-     * @return the line, or null if EOF reached before '\n' is seen.
-     * @throws IOException if an error occurs.
-     */
-    private String readLine(RandomAccessFile reader) throws IOException {
-        StringBuffer sb  = new StringBuffer();
-        int ch;
+        int num;
         boolean seenCR = false;
-        while((ch=reader.read()) != -1) {
-            switch(ch) {
+        while (run && ((num = reader.read(inbuf)) != -1)) {
+            for (int i = 0; i < num; i++) {
+                byte ch = inbuf[i];
+                switch (ch) {
                 case '\n':
-                    return sb.toString();
+                    listener.handle(sb.toString());
+                    sb = new StringBuilder();
+                    rePos = pos + i + 1;
+                    break;
                 case '\r':
                     seenCR = true;
                     break;
@@ -368,9 +395,15 @@ public class Tailer implements Runnable 
                         sb.append('\r');
                         seenCR = false;
                     }
-                    sb.append((char)ch); // add character, not its ascii value
+                    sb.append((char) ch); // add character, not its ascii value
+                }
             }
+
+            pos = reader.getFilePointer();
         }
-        return null;
+
+        reader.seek(rePos); // Ensure we can re-read if necessary
+        return rePos;
     }
+
 }


Reply via email to