File Changed Listener in Java

File changed listener in Java

Since JDK 1.7, the canonical way to have an application be notified of changes to a file is using the WatchService API. The WatchService is event-driven. The official tutorial provides an example:

/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.LinkOption.*;
import java.nio.file.attribute.*;
import java.io.*;
import java.util.*;

/**
* Example to watch a directory (or tree) for changes to files.
*/

public class WatchDir {

private final WatchService watcher;
private final Map<WatchKey,Path> keys;
private final boolean recursive;
private boolean trace = false;

@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>)event;
}

/**
* Register the given directory with the WatchService
*/
private void register(Path dir) throws IOException {
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
if (trace) {
Path prev = keys.get(key);
if (prev == null) {
System.out.format("register: %s\n", dir);
} else {
if (!dir.equals(prev)) {
System.out.format("update: %s -> %s\n", prev, dir);
}
}
}
keys.put(key, dir);
}

/**
* Register the given directory, and all its sub-directories, with the
* WatchService.
*/
private void registerAll(final Path start) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
register(dir);
return FileVisitResult.CONTINUE;
}
});
}

/**
* Creates a WatchService and registers the given directory
*/
WatchDir(Path dir, boolean recursive) throws IOException {
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<WatchKey,Path>();
this.recursive = recursive;

if (recursive) {
System.out.format("Scanning %s ...\n", dir);
registerAll(dir);
System.out.println("Done.");
} else {
register(dir);
}

// enable trace after initial registration
this.trace = true;
}

/**
* Process all events for keys queued to the watcher
*/
void processEvents() {
for (;;) {

// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}

Path dir = keys.get(key);
if (dir == null) {
System.err.println("WatchKey not recognized!!");
continue;
}

for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind kind = event.kind();

// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}

// Context for directory entry event is the file name of entry
WatchEvent<Path> ev = cast(event);
Path name = ev.context();
Path child = dir.resolve(name);

// print out event
System.out.format("%s: %s\n", event.kind().name(), child);

// if directory is created, and watching recursively, then
// register it and its sub-directories
if (recursive && (kind == ENTRY_CREATE)) {
try {
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
registerAll(child);
}
} catch (IOException x) {
// ignore to keep sample readbale
}
}
}

// reset key and remove from set if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
keys.remove(key);

// all directories are inaccessible
if (keys.isEmpty()) {
break;
}
}
}
}

static void usage() {
System.err.println("usage: java WatchDir [-r] dir");
System.exit(-1);
}

public static void main(String[] args) throws IOException {
// parse arguments
if (args.length == 0 || args.length > 2)
usage();
boolean recursive = false;
int dirArg = 0;
if (args[0].equals("-r")) {
if (args.length < 2)
usage();
recursive = true;
dirArg++;
}

// register directory and process its events
Path dir = Paths.get(args[dirArg]);
new WatchDir(dir, recursive).processEvents();
}
}

For individual files, various solutions exist, such as:

  • https://dzone.com/articles/listening-to-fileevents-with-java-nio

Note that Apache VFS uses a polling algorithm, although it may offer greater functionality. Also note that the API does not offer a way to determine whether a file has been closed.

Watching for file and directory changes in Java

This will allow you to experiment with creating, deleting, moving and renaming files under D:\Temp, and should allow you to learn what you need:

import static com.sun.nio.file.ExtendedWatchEventModifier.FILE_TREE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;

import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class Foo3
{
public static void main(String[] args) throws Exception
{
FileSystem fs = FileSystems.getDefault();
WatchService ws = fs.newWatchService();
Path pTemp = Paths.get("D:/Temp");
pTemp.register(ws, new WatchEvent.Kind[] {ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE}, FILE_TREE);
while(true)
{
WatchKey k = ws.take();
for (WatchEvent<?> e : k.pollEvents())
{
Object c = e.context();
System.out.printf("%s %d %s\n", e.kind(), e.count(), c);
}
k.reset();
}
}
}

Can I watch for single file change with WatchService (not the whole directory)?

Just filter the events for the file you want in the directory:

final Path path = FileSystems.getDefault().getPath(System.getProperty("user.home"), "Desktop");
System.out.println(path);
try (final WatchService watchService = FileSystems.getDefault().newWatchService()) {
final WatchKey watchKey = path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
final WatchKey wk = watchService.take();
for (WatchEvent<?> event : wk.pollEvents()) {
//we only register "ENTRY_MODIFY" so the context is always a Path.
final Path changed = (Path) event.context();
System.out.println(changed);
if (changed.endsWith("myFile.txt")) {
System.out.println("My file has changed");
}
}
// reset the key
boolean valid = wk.reset();
if (!valid) {
System.out.println("Key has been unregisterede");
}
}
}

Here we check whether the changed file is "myFile.txt", if it is then do whatever.

java Tracking changes inside a txt file

There is no such API in the Java standard library. The WatchService API allows you to detect when your .txt file is changed, but detecting which line is changed is something that you'll need to implement yourself.

In order to do that, you need to store the contents of the file as a list of lines, and when you receive a change notification, read the file again and compare the new contents with the contents you've previously stored.

File text change detection with rxjava

Of course it's possible. I'd do that in the following way:

  • Use FileAlterationObserver from the Apache Commons I/O
    library
  • Create new observer basing on the documentation linked above
    (you can filter a concrete file if you don't want to monitor whole
    directory)
  • In the observer you can use FileAlterationListener, which has methods like onFileChange(File) required for this task
  • Create a method returning RxJava Flowable (backpressure-aware) or Observable type, which wraps listener created above - this will require some RxJava knowledge - you'll need to invoke onNext(...) method on the Emitter inside the Flowable implementation and define the type, you need to return within the Flowable - e.g. Flowable<FileEvent> where FileEvent have to be defined

How to listen on file directory if file changed/added/deletd in java 6

As far as I know, this isn't possible in Java 6. Your best shot is to go for Apache VFS.

JTabbedPane change listener to read from file

It's not the responsibility of the ChangeListener to be making decisions about what should be done when a tab is selected. Instead, you need some way to instruct the selected tab that it should perform what ever operations it needs to when selected. This decouples the code and allows you to change the order of the tabs without affect the remaining code.

Start by defining a simple concept of something which is loadable...

public interface Loadable {
public void load();
}

Then each tab/panel you want to be notified when the tab selection changes should implement this interface

public class LoadableTabPane extends JPanel implements Loadabel {
//...
public void load() {
// Performing the loading here
}
}

Then when the tab selection changes, you inspect the selected component to determine of it's an instanceof Loadable and if it is, you cast it and call its load method...

tp.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
Component selectedComponent = tp.getSelectedComponent();
if (selectedComponent instanceof Loadable) {
((Loadable)selectedComponent).load();
}
}
}


Related Topics



Leave a reply



Submit