Im trying to implement the simple tail -f linux command in java. Here is my code.
try
{
// position within the file
File file = new File("/home/curuk/monitored/log.txt");
RandomAccessFile raFile = new RandomAccessFile(file, "r");
long last = file.lastModified(); // The last time the file was checked for changes
long position = file.length();
while (true)
{
if (file.lastModified() > last)
{
last = file.lastModified();
readFromFile(raFile, (int) position, (int) (file.length() - position));
position = file.length();
}
Thread.sleep(1000);
}
}
catch (IOException e)
{
e.printStackTrace();
}
private byte[] readFromFile(RandomAccessFile file, int position, int size) throws IOException
{
file.seek(position);
byte[] bytes = new byte[size];
System.err.println(file.read(bytes, 0, size));
String s = new String(bytes);
System.err.println(s);
return bytes;
}
The problem is that under linux OS, file.read(bytes, 0, size)
always returns -1, while under Windows the same snippet of code works just fine (Always prints the new line).
Edit:
I solved the problem by adding raFile = new RandomAccessFile(file, "r");
at every iteration.
while (true)
{
raFile = new RandomAccessFile(file, "r");
if (file.lastModified() > last)
{
last = file.lastModified();
readFromFile(raFile, (int) position, (int) (file.length() - position));
position = file.length();
}
Thread.sleep(1000);
}
Don't know why, but now works fine under Linux as well.
Thanks for you effort guys
Here is a solution entirely based on Java 7, using the new WatchService
infrastructure:
Works, but quite crude...
public final class Baz
{
public static void main(final String... args)
throws IOException
{
// Get paths to the containing directory and the file we want to spy
final Path dir = Paths.get("/tmp");
final Path file = dir.resolve("foo.txt");
// Get the watch service for our default filesystem
// We are only interested in modifications and deletions
final WatchService service = FileSystems.getDefault().newWatchService();
dir.register(service, StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
// Get a charset decoder -- we will be reading bytes from the file,
// we will need to decode them to a string
final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder()
.onMalformedInput(CodingErrorAction.IGNORE)
.onUnmappableCharacter(CodingErrorAction.IGNORE);
try (
// Open a SeekableByteChannel to the file -- read only
final SeekableByteChannel channel
= Files.newByteChannel(file, StandardOpenOption.READ);
) {
long oldSize;
while (true) {
// Get the current size of our file
oldSize = channel.size();
try {
// Grab a key
final WatchKey key = service.poll(1L, TimeUnit.SECONDS);
if (key == null) // No events...
continue;
for (final WatchEvent<?> e: key.pollEvents()) {
@SuppressWarnings("unchecked")
final WatchEvent<Path> event = (WatchEvent<Path>) e;
// What kind of event, to whom it applies
final WatchEvent.Kind<Path> kind = event.kind();
final Path context = dir.resolve(event.context());
// If not to us, we don't care
if (!context.equals(file))
continue;
// If our file has disappeared, exit
if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
System.err.println("File deleted");
System.exit(0);
}
// OK, so it's a modification, and it is our file: read the tail
doRead(oldSize, decoder, channel);
}
// Reset the key for the next batch of events
key.reset();
} catch (InterruptedException e) {
// service.poll() interrupted: get out
break;
}
}
}
}
private static void doRead(final long oldSize, final CharsetDecoder decoder,
final SeekableByteChannel channel)
throws IOException
{
final long newSize = channel.size();
if (newSize <= oldSize)
return;
final int bufsize = (int) (newSize - oldSize);
final ByteBuffer buf = ByteBuffer.allocate(bufsize);
channel.position(oldSize).read(buf);
buf.rewind();
decoder.reset();
System.out.println(decoder.decode(buf).toString());
}
}