ZOFTINO.COM android and web dev tutorials

Java File System Watcher Example

Java NIO API provides framework that allows you to create components which can monitor file system for changes. This feature is useful if your app needs to know changes to certain files and directories to provide functionality in your application. The components involved in watching a file system (watchable) are watch service, watch event and watch key.

Watch service watches the registered objects for changes. In the case of file system, file system’s watch service is registered with a directory object for changes such as create, delete and modify watch events. Any changes such as creating, modifying and deleting a file or directory under the directory being watched will trigger watch event which can be handled to perform actions such as reading the file in response to it.

Flow and Steps in Watching File System and Handling Watch Events

First create path object of the directory you want to watch changes for using Paths class.

Path filePath = Paths.get("/dev/java/core/app/");

Then get file system watcher service object by using FileSystems class. To do that, get default file system object by calling getDefault() static method on FileSystems and then call newWatchService() method on FileSystem object.

WatchService watchService = FileSystems.getDefault().newWatchService();

Then register the directory object with watch service. To do that, you need to call register method on the path object which represents the directory being watched for changes. The register() method takes watch service and set of event kinds such as StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE and StandardWatchEventKinds.ENTRY_MODIFY as arguments and returns watch key object that represents the registration of the object with the watch service.

WatchKey maintains state. When a key is created, it is in ready state. When events occur on the associated object, the key will be in signaled state. Calling reset() method takes the signaled state key back to ready state.

filePath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,  
		StandardWatchEventKinds.ENTRY_DELETE, 
		StandardWatchEventKinds.ENTRY_MODIFY);

When an event for a registered object occurs, the corresponding key is added to the watch service queue, at this point key is in signaled state. The signaled key from the queue can be obtained for processing events using take or poll methods of watch service. The difference between poll() and take() is that take() method waits for the key to be available in the watch service queue if the key is not there in the queue. Method poll() returns the key if present else returns null if not present. Both methods remove the key from the queue before returning it. Once events are processed, watch key needs to be reset by calling reset() method so that it can be re-queued for future events, after reset, key is back to ready state.

WatchKey key = watchService.take();

Events can be obtained using pollEvents() method of watch key object. For each event, you can get event information such as event type or watch event kind info and the object that caused the event.

for (WatchEvent<?> event : key.pollEvents()) {
        WatchEvent.Kind<?> kind = event.kind();        
         
        System.out.println("kind "+ kind.type().getName());
        Path path = (Path)event.context();
        System.out.println(path.toString());
    }

To remove the registration, you can call cancel() method.

Java File System Watcher Example

The below example contains two parts. First part registers a directory with watch service and watches for changes in directory. second part writes content to a file which makes the watch service trigger event leading processing of the changes.

public class FileWatcher {
	private static final String folder =  "/dev/java/core/app/";

	public static void watchOffers() {

		Path filePath = Paths.get(folder);

		WatchService watchService;
		try {
			watchService = FileSystems.getDefault().newWatchService();

			//listen for create ,delete and modify event kinds
			filePath.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
					StandardWatchEventKinds.ENTRY_DELETE,
					StandardWatchEventKinds.ENTRY_MODIFY);
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}

		//
		while (true) {
			WatchKey key;
			try {
				//return signaled key, meaning events occurred on the object
				key = watchService.take();
			} catch (InterruptedException ex) {
				return;
			}

			//retrieve all the accumulated events
			for (WatchEvent<?> event : key.pollEvents()) {
				WatchEvent.Kind<?> kind = event.kind();               
				
				System.out.println("kind "+ kind.name());
				Path path = (Path)event.context();
				System.out.println(path.toString());
				
				if(kind.equals(StandardWatchEventKinds.ENTRY_MODIFY)) {
					readOffer(path.toString());
				}
			}             
			//resetting the key goes back ready state
			key.reset();
		}

	}
	public static void writeOffer(String fileName, String offer) {
		Path filePath = Paths.get(folder+fileName);		

		ByteBuffer bb = ByteBuffer.wrap(offer.getBytes());

		try (FileChannel fc = (FileChannel.open(filePath,StandardOpenOption.WRITE,
				StandardOpenOption.TRUNCATE_EXISTING))) {
			bb.rewind();
			fc.write(bb);
		} catch (IOException ex) {
			System.out.println("exception in writing to offers file: "+ex);
		}
	}
	public static void readOffer(String fileName) {
		Path filePath = Paths.get(folder+fileName);

		ByteBuffer bb = ByteBuffer.allocate(100);

		Charset cset =  Charset.forName("UTF-8");
		StringBuilder sb = new StringBuilder();
		String off;
		try (FileChannel fc = (FileChannel.open(filePath,
				StandardOpenOption.READ))) {

			while (fc.read(bb) > 0) {
				bb.rewind();
				off  = cset.decode(bb).toString();
				sb.append(off);
				System.out.print(off);
				bb.flip();
			}
		} catch (IOException ex) {
			System.out.println("exception in reading file: "+ex);
			ex.printStackTrace();
		}
	}
}

First start the watcher program which watches a directory for changes.

	public static void main(String[] args) {
		 FileWatcher.watchOffers();
	}

Then run the program to write to a file in the directory being watched. This causes triggering of watch event and executing the event processing.

	public static void main(String[] args) {		
		String offer = "90% off on footwear,";
		FileWatcher.writeOffer("offers.txt", offer);
	}

Other Tutorials Related to IO