All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.io.input.Tailer Maven / Gradle / Ivy

There is a newer version: 1.1.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.commons.io.input;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

/**
 * Simple implementation of the unix "tail -f" functionality.
 * 

*

1. Create a TailerListener implementation

*

* First you need to create a {@link TailerListener} implementation * ({@link TailerListenerAdapter} is provided for convenience so that you don't have to * implement every method). *

* *

For example:

*
 *  public class MyTailerListener extends TailerListenerAdapter {
 *      public void handle(String line) {
 *          System.out.println(line);
 *      }
 *  }
 * 
* *

2. Using a Tailer

* * You can create and use a Tailer in one of three ways: *
    *
  • Using one of the static helper methods: *
      *
    • {@link Tailer#create(File, TailerListener)}
    • *
    • {@link Tailer#create(File, TailerListener, long)}
    • *
    • {@link Tailer#create(File, TailerListener, long, boolean)}
    • *
    *
  • *
  • Using an {@link java.util.concurrent.Executor}
  • *
  • Using an {@link Thread}
  • *
* * An example of each of these is shown below. * *

2.1 Using the static helper method

* *
 *      TailerListener listener = new MyTailerListener();
 *      Tailer tailer = Tailer.create(file, listener, delay);
 * 
* *

2.2 Use an Executor

* *
 *      TailerListener listener = new MyTailerListener();
 *      Tailer tailer = new Tailer(file, listener, delay);
 *
 *      // stupid executor impl. for demo purposes
 *      Executor executor = new Executor() {
 *          public void execute(Runnable command) {
 *              command.run();
 *           }
 *      };
 *
 *      executor.execute(tailer);
 * 
* * *

2.3 Use a Thread

*
 *      TailerListener listener = new MyTailerListener();
 *      Tailer tailer = new Tailer(file, listener, delay);
 *      Thread thread = new Thread(tailer);
 *      thread.setDaemon(true); // optional
 *      thread.start();
 * 
* *

3. Stop Tailing

*

Remember to stop the tailer when you have done with it:

*
 *      tailer.stop();
 * 
* * @see TailerListener * @see TailerListenerAdapter * @version $Id: Tailer.java 1127854 2011-05-26 10:03:42Z sebb $ * @since Commons IO 2.0 */ public class Tailer implements Runnable { /** * The file which will be tailed. */ private final File file; /** * The amount of time to wait for the file to be updated. */ private final long delay; /** * Whether to tail from the end or start of file */ private final boolean end; /** * The listener to notify of events when tailing. */ private final TailerListener listener; /** * The tailer will run as long as this value is true. */ private volatile boolean run = true; /** * Creates a Tailer for the given file, starting from the beginning, with the default delay of 1.0s. * @param file The file to follow. * @param listener the TailerListener to use. */ public Tailer(File file, TailerListener listener) { this(file, listener, 1000); } /** * Creates a Tailer for the given file, starting from the beginning. * @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. */ public Tailer(File file, TailerListener listener, long delay) { this(file, listener, 1000, false); } /** * Creates a Tailer for the given file, with a delay other than the default 1.0s. * @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. */ public Tailer(File file, TailerListener listener, long delay, boolean end) { this.file = file; this.delay = delay; this.end = end; // Save and prepare the listener this.listener = listener; listener.init(this); } /** * Creates and starts a Tailer for the given file. * * @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) { Tailer tailer = new Tailer(file, listener, delay, end); Thread thread = new Thread(tailer); thread.setDaemon(true); thread.start(); return tailer; } /** * Creates and starts a Tailer for the given file, starting at the beginning of the file * * @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. * @return The new tailer */ public static Tailer create(File file, TailerListener listener, long delay) { return create(file, listener, delay, false); } /** * Creates and starts a Tailer for the given file, starting at the beginning of the file * with the default delay of 1.0s * * @param file the file to follow. * @param listener the TailerListener to use. * @return The new tailer */ public static Tailer create(File file, TailerListener listener) { return create(file, listener, 1000, false); } /** * Return the file. * * @return the file */ public File getFile() { return file; } /** * Return the delay. * * @return the delay */ public long getDelay() { return delay; } /** * Follows changes in the file, calling the TailerListener's handle method for each new line. */ public void run() { RandomAccessFile reader = null; try { long last = 0; // The last time the file was checked for changes long position = 0; // position within the file // Open the file while (run && reader == null) { try { reader = new RandomAccessFile(file, "r"); } catch (FileNotFoundException e) { listener.fileNotFound(); } if (reader == null) { try { Thread.sleep(delay); } catch (InterruptedException e) { } } else { // The current position in the file position = end ? file.length() : 0; last = System.currentTimeMillis(); reader.seek(position); } } while (run) { // Check the file length to see if it was rotated long length = file.length(); if (length < position) { // File was rotated listener.fileRotated(); // Reopen the reader after rotation try { // Ensure that the old file is closed iff we re-open it successfully RandomAccessFile save = reader; reader = new RandomAccessFile(file, "r"); position = 0; // close old file explicitly rather than relying on GC picking up previous RAF IOUtils.closeQuietly(save); } catch (FileNotFoundException e) { // in this case we continue to use the previous reader and position values listener.fileNotFound(); } continue; } else { // File was not rotated // See if the file needs to be read again if (length > position) { // The file has more content than it did last time last = System.currentTimeMillis(); position = readLines(reader); } 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 */ position = 0; reader.seek(position); // cannot be null here // Now we can read new lines last = System.currentTimeMillis(); position = readLines(reader); } } try { Thread.sleep(delay); } catch (InterruptedException e) { } } } catch (Exception e) { listener.handle(e); } finally { IOUtils.closeQuietly(reader); } } /** * Allows the tailer to complete its current loop and return. */ public void stop() { this.run = false; } /** * Read new lines. * * @param reader The file to read * @return The new position after the lines have been read * @throws java.io.IOException if an I/O error occurs. */ private long readLines(RandomAccessFile reader) throws IOException { 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; } /** * 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; boolean seenCR = false; while((ch=reader.read()) != -1) { switch(ch) { case '\n': return sb.toString(); case '\r': seenCR = true; break; default: if (seenCR) { sb.append('\r'); seenCR = false; } sb.append((char)ch); // add character, not its ascii value } } return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy