Search code examples
javaniorunnable

How can I convert WatchService into a Runnable?


I have a watch service that monitors a directory for ENTRY_DELETE,ENTRY_CREATE and ENTRY_MODIFY events and performs logic based upon the event.

I need the service to watch the directory for all changes, and not exit the loop. But I also need to launch into my other logic.

How can I refactor these methods into a Runnable to accomplish this?

Below if the code.

public static void main(String[] args) {        
    System.out.println("Started watching");
    FileServices fileServices = new FileServicesImpl();

    fileServices.setSrcDir(fileServices.getValue("srcdir","properties/abc.properties"));
    fileServices.setDestDir(fileServices.getValue("destdir","properties/abc.properties"));

    System.out.println(fileServices.getSrcDir());
    System.out.println(fileServices.getDestDir());

    Map<String,WatchEvent> files = new HashMap<>();
    MappingConsole mappingConsole = new MappingConsole();

    for(;;){
        files = fileServices.getEventMap();
        for(String f : files.keySet()){
            System.out.println("Size of files: "+files.size());
            if (files.get(f).kind() == ENTRY_CREATE || files.get(f).kind() == ENTRY_MODIFY) {
                System.out.println("Processing: " +f);
                mappingConsole.map940(fileServices.getSrcDir(),f,fileServices.getDestDir());
                System.out.println("Processed: " +f);
            }
        }
    }

}

From FileServicesImpl:

@Override
public void monitorSrcDir(String srcDir){
    for(;;){
        try {
            WatchService watchService = FileSystems.getDefault().newWatchService();
            Path myDir = Paths.get(srcDir);
            WatchService watcher = myDir.getFileSystem().newWatchService();
            myDir.register(watcher, ENTRY_CREATE,ENTRY_DELETE, ENTRY_MODIFY);
            WatchKey watchKey = watcher.take();
            List<WatchEvent<?>> events = watchKey.pollEvents();
            for (WatchEvent event : events) {
                if (event.kind() == ENTRY_CREATE) {
                    System.out.println("Create: " + event.context().toString());
                    getEventMap().put(event.context().toString(), event);
                }
                if (event.kind() == ENTRY_DELETE) {
                    System.out.println("Delete: " + event.context().toString());
                    getEventMap().put(event.context().toString(), event);
                }
                if (event.kind() == ENTRY_MODIFY) {
                    System.out.println("Modify: " + event.context().toString());
                    getEventMap().put(event.context().toString(), event);
                }
            }
            watchKey.reset();
        }  catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Solution

  • I do this:

    public class WatchServiceMonitor implements Monitor, Runnable, Closeable {
        private static Logger log = LoggerFactory.getLogger( WatchServiceMonitor.class.getName() );
        private Destination destination;
        private Path hotFolder;
        private Thread thread;
    
        public WatchServiceMonitor( Path hotFolder, Destination destination ) {
            this.hotFolder = hotFolder;
            this.destination = destination;
    
        }
    
        @Override
        public void close() throws IOException {
            try {
                stop();
            }
            catch ( InterruptedException e ) {
                log.warn( "request to stop failed, guess its time to stop being polite!" );
            }
        }
    
        @Override
        public void join() throws InterruptedException {
            thread.join();
        }
    
        @Override
        public void run() {
            try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
                if ( log.isTraceEnabled() ) log.trace( "registering create watcher on " + hotFolder.toAbsolutePath().toString() );
                hotFolder.register( watcher, StandardWatchEventKinds.ENTRY_CREATE );
                if ( log.isDebugEnabled() ) log.debug( "watcher registration complete for " + hotFolder.toAbsolutePath().toString() );
                synchronized ( this ) {
                    this.notifyAll();
                }
                for ( ;; ) {
                    if ( thread.isInterrupted() ) break;
    
                    WatchKey key = null;
                    try {
                        log.trace( "waiting for create event" );
                        key = watcher.take();
                        log.trace( "got an event, process it" );
                    }
                    catch ( InterruptedException ie ) {
                        log.trace( "interruped, must be time to shut down..." );
                        break;
                    }
    
                    for ( WatchEvent<?> eventUnknown : key.pollEvents() ) {
                        WatchEvent.Kind<?> kind = eventUnknown.kind();
    
                        if ( kind == StandardWatchEventKinds.OVERFLOW ) return;
    
                        @SuppressWarnings( "unchecked" )
                        WatchEvent<Path> eventPath = (WatchEvent<Path>) eventUnknown;
                        Path path = hotFolder.resolve( eventPath.context() );
                        log.trace( "calling destination.transfer({})", path );
                        destination.transfer( path );
                        log.info( "transferred {} to destination" );
    
                        if (! key.reset()) {
                            break;
                        }
                    }
                }
            }
            catch ( IOException ioe ) {
                log.error( ioe.getMessage(), ioe );
            }
            log.debug( "existing run loop" );
        }
    
        @Override
        public void start() throws InterruptedException {
            log.trace( "starting monitor" );
            thread = new Thread( this );
            thread.start();
            synchronized ( this ) {
                this.wait();
            }
            log.trace( "monitor started" );
        }
    
        @Override
        public void stop() throws InterruptedException {
            log.trace( "stopping monitor" );
            thread.interrupt();
            thread.join();
            thread = null;
            log.trace( "monitor stopped" );
        }
    }
    

    The Destination class is one of my own that knows how to transfer from the path indicated by the WatchService event.

    So, in essence I just wrapped the whole WatchService loop in the run method of a Runnable instance.