Directory Watcher
The following two tabs change content below.
Hi, I have written and developed this site to share my experience and ideas with other colleagues. I also started to prepare interview questions and answers for job seekers. I hope it will help you a lot.

Today I just came up with solution regarding Directory Watcher and to check File copy progress and get notification once file copied. So in this post we are going to follow two examples:

1. Directory Watcher

2. File copy progress

Full code you can findout from GIT account 

Directory watcher : The java.nio.file package provides a file change notification API, called the Watch Service API. This API enables you to register a directory (or directories) with the watch service. When registering, you tell the service which types of events you are interested in: file creation, file deletion, or file modification. When the service detects an event of interest, it is forwarded to the registered process. The registered process has a thread (or a pool of threads) dedicated to watching for any events it has registered for. When an event comes in, it is handled as needed.

To intigrate WatchService API we need to follow below points:

1. Create a WatchService “watcher” for the file system.

this.watcher = FileSystems.getDefault().newWatchService();

2. For each directory that you want monitored, register it with the watcher. When registering a directory, you specify the type of events for which you want notification. You receive a WatchKey instance for each directory that you register.

 // register directory and sub-directories
 Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
 @Override
 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
 registerDirectory(dir);
 return FileVisitResult.CONTINUE;
 }
 });

Events:

java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;

3. Implement an infinite loop to wait for incoming events. When an event occurs, the key is signaled and placed into the watcher’s queue.

/**
 * 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()) {
 @SuppressWarnings("rawtypes")
 WatchEvent.Kind kind = event.kind();
 
 // Context for directory entry event is the file name of entry
 @SuppressWarnings("unchecked")
 Path name = ((WatchEvent<Path>)event).context();
 Path child = dir.resolve(name);


//This event will be trigger 2 time once we initiate the file copy and 2 second file copy completed 
 
 if (kind == ENTRY_MODIFY){
 //adding same event to a list to that we can compare using frequency
 eventList.add((event.kind().name() + child.getFileName().toString()).trim());
 
 //If file transfer successfully then this condition will be true
 if(Collections.frequency(eventList, (event.kind().name() + child.getFileName().toString()).trim()) == 2){
 System.out.println("------------------- File transfer ------");
 eventList.clear();
 }
 }
 
 // Clearing the list if file deleted or cancel
 if (kind == ENTRY_DELETE){
 System.out.println("--File Deleted---");
 eventList.clear();
 }
 
 
 // if directory is created, and watching recursively, then register it and its sub-directories
 if (kind == ENTRY_CREATE) {
 try {
 if (Files.isDirectory(child)) {
 walkAndRegisterDirectories(child);
 }
 } catch (IOException x) {
 // do something useful
 }
 }
 }
 
 // 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;
 }
 }
 }
 }

4. Retrieve the key from the watcher’s queue. You can obtain the file name from the key.

for (WatchEvent<?> event : key.pollEvents()) {
 @SuppressWarnings("rawtypes")
 WatchEvent.Kind kind = event.kind();
 
 // Context for directory entry event is the file name of entry
 @SuppressWarnings("unchecked")
 Path name = ((WatchEvent<Path>)event).context();
 Path child = dir.resolve(name);
 ........

5. Retrieve each pending event for the key (there might be multiple events) and process as needed.

6. Reset the key, and resume waiting for events.

eventList.clear();

7. Close the service: The watch service exits when either the thread exits or when it is closed (by invoking its closed method).

 

Here is the full code of Directory Watcher:

package com.kpblogs;

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.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class DirectoryWatcherService {
 
 private final WatchService watcher;
 private final Map<WatchKey, Path> keys;
 private List<String> eventList = new ArrayList<String>();
 /**
 * Creates a WatchService and registers the given directory
 */
 DirectoryWatcherService(Path dir) throws IOException {
 this.watcher = FileSystems.getDefault().newWatchService();
 this.keys = new HashMap<WatchKey, Path>();
 
 walkAndRegisterDirectories(dir);
 }
 
 /**
 * Register the given directory with the WatchService; This function will be called by FileVisitor
 */
 private void registerDirectory(Path dir) throws IOException
 {
 WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
 keys.put(key, dir);
 }
 
 /**
 * Register the given directory, and all its sub-directories, with the WatchService.
 */
 private void walkAndRegisterDirectories(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 {
 registerDirectory(dir);
 return FileVisitResult.CONTINUE;
 }
 });
 }
 
 /**
 * 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()) {
 @SuppressWarnings("rawtypes")
 WatchEvent.Kind kind = event.kind();
 
 // Context for directory entry event is the file name of entry
 @SuppressWarnings("unchecked")
 Path name = ((WatchEvent<Path>)event).context();
 Path child = dir.resolve(name);


//This event will be trigger 2 time once we initiate the file copy and 2 second file copy completed 
 
 if (kind == ENTRY_MODIFY){
 //adding same event to a list to that we can compare using frequency
 eventList.add((event.kind().name() + child.getFileName().toString()).trim());
 
 //If file transfer successfully then this condition will be true
 if(Collections.frequency(eventList, (event.kind().name() + child.getFileName().toString()).trim()) == 2){
 System.out.println("------------------- File transfer ------");
 eventList.clear();
 }
 }
 
 
 // Clearing the list if file deleted or cancel
 if (kind == ENTRY_DELETE){
 System.out.println("--File Deleted---");
 eventList.clear();
 }
 
 
 // if directory is created, and watching recursively, then register it and its sub-directories
 if (kind == ENTRY_CREATE) {
 try {
 if (Files.isDirectory(child)) {
 walkAndRegisterDirectories(child);
 }
 } catch (IOException x) {
 // do something useful
 }
 }
 }
 
 // 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;
 }
 }
 }
 }
 
 public static void main(String[] args) throws IOException {
 Path dir = Paths.get("c:/kpblogs/");
 new DirectoryWatcherService(dir).processEvents();

 }
}

Directory Watcher Output

 

File copy progress: In this example going to check copy file progress (means how much file copied in % or file copied completed or not) so based on that we can fire the event/action. This functionality we are going to achive by ReadableByteChannel. 

ReadableByteChannel: A channel that can read bytes. Only one read operation upon a readable channel may be in progress at any given time. If one thread initiates a read operation upon a channel then any other thread that attempts to initiate another read operation will block until the first operation is complete. Whether or not other kinds of I/O operations may proceed concurrently with a read operation depends upon the type of the channel.

To achive this written the copy method as mention below:

 public static void copy(String source, String destination, ProgressCallBack callBack) throws IOException {
 FileOutputStream fos = null;
 FileChannel sourceChannel = null;
 try {
 sourceChannel = new FileInputStream(new File(source)).getChannel();
 ReadableByteChannel rbc = new CallbackByteChannel(sourceChannel, Files.size(Paths.get(source)), callBack);
 fos = new FileOutputStream(destination);
 fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 if (sourceChannel.isOpen()) {
 sourceChannel.close();
 }
 fos.close();
 }
 }

This method accepting three parameter source (from where we are coping the file), destination (where we are pasting source given file name) and callback is used as delegate to show the progress of copy file. internally in copy we are calling CallbackByteChannel  class. Here is the full example class:

package com.kpblogs;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * 
 * @author kpal
 *
 */
interface ProgressCallBack {
 public void callback(CallbackByteChannel rbc, double progress);
}

public class FileCopyProgress implements ReadableByteChannel {
 public static void main(String[] args) {

ProgressCallBack callBack = new ProgressCallBack() {

public void callback(CallbackByteChannel rbc, double progress) {
 //System.out.println(rbc.getReadSoFar());
 System.out.println(progress);

if (progress == 100) {
 System.out.println("File copied complted");
 // Write the logic here it will be called once file copied completed
 }

}
 };

try {
 copy("c:\\kpblogs\\src\\Watcher.png", "c:\\kpblogs\\dest\\Watcher.png", callBack);
 } catch (IOException e) {
 e.printStackTrace();
 }
 }

@SuppressWarnings("resource")
 public static void copy(String source, String destination, ProgressCallBack callBack) throws IOException {
 FileOutputStream fos = null;
 FileChannel sourceChannel = null;
 try {
 sourceChannel = new FileInputStream(new File(source)).getChannel();
 ReadableByteChannel rbc = new CallbackByteChannel(sourceChannel, Files.size(Paths.get(source)), callBack);
 fos = new FileOutputStream(destination);
 fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 if (sourceChannel.isOpen()) {
 sourceChannel.close();
 }
 fos.close();
 }
 }

public void close() throws IOException {
 // TODO Auto-generated method stub

}

public boolean isOpen() {
 // TODO Auto-generated method stub
 return false;
 }

public int read(ByteBuffer arg0) throws IOException {
 // TODO Auto-generated method stub
 return 0;
 }
}

class CallbackByteChannel implements ReadableByteChannel {
 ProgressCallBack delegate;
 long size;
 ReadableByteChannel rbc;
 long sizeRead;

CallbackByteChannel(ReadableByteChannel rbc, long expectedSize, ProgressCallBack delegate) {
 this.delegate = delegate;
 this.size = expectedSize;
 this.rbc = rbc;
 }

public void close() throws IOException {
 rbc.close();
 }

public long getReadSoFar() {
 return sizeRead;
 }

public boolean isOpen() {
 return rbc.isOpen();
 }

public int read(ByteBuffer bb) throws IOException {
 int n;
 double progress;
 if ((n = rbc.read(bb)) > 0) {
 sizeRead += n;
 progress = size > 0 ? (double) sizeRead / (double) size * 100.0 : -1.0;
 delegate.callback(this, progress);
 }
 return n;
 }
}

File copy progress

 

Full code you can findout from GIT account 

331 total views, 5 views today

Leave a Reply

Your email address will not be published. Required fields are marked *