Java RandomAccessFile.java under linux not working

2019-02-27 19:08发布

问题:

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

回答1:

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());
    }
}