How to watch a folder/directory for changes using Java?

Photo by Pixabay on Pexels.com

Let’s say you want to watch a folder in your computer or server for any changes.

If a new file gets added you want to be notified.

If an existing file is modified you want to be notified.

If a file is deleted you want to be notified.

And how do you do this without blocking your current thread of execution?

Let’s look into them in detail.

First let’s look how to do it in a blocking way.

To do this you need to use Watch API provided by java in java.nio package.

Here is the algorithm to implement it:

STEP1: Create a watch service

STEP2: Get the path of the folder you want to monitor

STEP3: Register the folder path with the service

STEP4: Poll for events in an infinite loop

STEP5: For each event get the file name from the event context

STEP6: For each event check its type

STEP7: For each type of event perform the action you want.

STEP8: Reset the watch key used to poll events.

Here are the steps in detail:

STEP1: Create a watch service.

Create a watch service using the below java code:

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

STEP2: Get the path of the folder you want to monitor.

You can do this using Path class like below:

Path directory = Path.of("G:\\projects\\fullstackdeveloperblog\\watchapi");

STEP3: Register the folder with the watch service.

You can monitor for three events using the watch service created.

  • File creation
  • File modification
  • File deletion

You can monitor your folder for all these three events by registering your folder with the watch service like below:

		WatchKey watchKey = directory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
					StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);

Once you register , you will get the watch key. You can use this to poll the folder for any change.

STEP4: Poll for events in an infinite loop.

You can use a while loop to pool infinitely for events :

while(true){

// polling logic here
}

STEP5: Get file name from event context.

Inside the infinite loop you can get the name of the file which was created/modified/deleted using the below code:

	WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;

       Path fileName = pathEvent.context();

STEP6: Check the type of event.

Once you get the file name check for the type of event which happened on the file using the below code:

	WatchEvent.Kind<?> kind = event.kind();

STEP7: Perform required action on the event occured.

Once you get the event type , do specific action for the event type:

	if (kind == StandardWatchEventKinds.ENTRY_CREATE) {

						System.out.println("A new file is created : " + fileName);
					}

					if (kind == StandardWatchEventKinds.ENTRY_DELETE) {

						System.out.println("A file has been deleted: " + fileName);
					}
					if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {

						System.out.println("A file has been modified: " + fileName);
					}

STEP8: Reset the watch key.

Finally reset the watch key so that you can continue to use it for polling (Even without using this step it works for me but it is advised by the java team to mandatorily follow this step)

boolean valid = watchKey.reset();
				if (!valid) {
					break;
				}

Here is the entire code:

import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class WatchFolder {

	public void watchFolder() {

		try {

			System.out.println("Watching directory for changes");

			// STEP1: Create a watch service
			WatchService watchService = FileSystems.getDefault().newWatchService();

			// STEP2: Get the path of the directory which you want to monitor.
			Path directory = Path.of("G:\\projects\\fullstackdeveloperblog\\watchapi");

			// STEP3: Register the directory with the watch service
			WatchKey watchKey = directory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
					StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);

			// STEP4: Poll for events
			while (true) {
				for (WatchEvent<?> event : watchKey.pollEvents()) {

					// STEP5: Get file name from even context
					WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;

					Path fileName = pathEvent.context();

					// STEP6: Check type of event.
					WatchEvent.Kind<?> kind = event.kind();

					// STEP7: Perform necessary action with the event
					if (kind == StandardWatchEventKinds.ENTRY_CREATE) {

						System.out.println("A new file is created : " + fileName);
					}

					if (kind == StandardWatchEventKinds.ENTRY_DELETE) {

						System.out.println("A file has been deleted: " + fileName);
					}
					if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {

						System.out.println("A file has been modified: " + fileName);
					}

				}

				// STEP8: Reset the watch key everytime for continuing to use it for further event polling
				boolean valid = watchKey.reset();
				if (!valid) {
					break;
				}

			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

Now let me test these changes.

I created a file in G:\projects\fullstackdeveloperblog\watchapi:

And the below got printed in the console:

I modified it:

And the event got reflected in my application:

I deleted the file:

And it got reflected as well:

As earlier mentioned this thread is blocking.

You can’t do anything else in your code while polling for file changes in your directory.

To do that , you need to create a new thread using Executor framework.

Follow the below steps:

STEP1: Create a callable class (implement Callable interface) and invoke the above method inside its call() method:

import java.util.concurrent.Callable;

public class WatchCallable implements Callable<Void> {

	@Override
	public Void call() throws Exception {

		WatchFolder wf = new WatchFolder();
		wf.watchFolder();

		return null;
	}

}

STEP2: Create an executor and submit the above Callable to it:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class WatchExecutor {

	public static void main(String a[]) {

		System.out.println("Starting a background thread for watching folders");
		ExecutorService executor = Executors.newCachedThreadPool();

		executor.submit(new WatchCallable());

		System.out.println("After submitting Callable for watching folder");
	}
}

That’s it!

Now you can watch for changes in your folder in a background thread!

Here is the link to the above (with non blocking implementation):

https://github.com/vijaysrj/watchapidemo

Ref:

https://docs.oracle.com/javase/tutorial/essential/io/notification.html


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading