/** * Creates a watcher that watches <code>dirPath</code> and invokes <code>callback</code> on changes. * * @param dirPath the directory to watch. * @param callback the callback to invoke with events. <code>event.kind()</code> will return the type of event, * and <code>event.context()</code> will return the filename relative to <code>dirPath</code>. * @throws IOException if there is an error creating the WatchService. */ public FileChangeWatcher(Path dirPath, Consumer<WatchEvent<?>> callback) throws IOException { FileSystem fs = dirPath.getFileSystem(); WatchService watchService = fs.newWatchService(); if (LOG.isDebugEnabled()) { LOG.debug("Registering with watch service: " + dirPath); } dirPath.register( watchService, new WatchEvent.Kind<?>[]{ StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.OVERFLOW}, SensitivityWatchEventModifier.HIGH); state = State.NEW; this.watcherThread = new WatcherThread(watchService, callback); this.watcherThread.setDaemon(true); }
} catch (InterruptedException|ClosedWatchServiceException e) { if (LOG.isDebugEnabled()) { LOG.debug(getName() + " was interrupted and is shutting down ...");
@Override public void run() { try { LOG.info(getName() + " thread started"); if (!compareAndSetState( FileChangeWatcher.State.STARTING, FileChangeWatcher.State.RUNNING)) { // stop() called shortly after start(), before // this thread started running. FileChangeWatcher.State state = FileChangeWatcher.this.getState(); if (state != FileChangeWatcher.State.STOPPING) { throw new IllegalStateException("Unexpected state: " + state); } return; } runLoop(); } catch (Exception e) { LOG.warn("Error in runLoop()", e); throw e; } finally { try { watchService.close(); } catch (IOException e) { LOG.warn("Error closing watch service", e); } LOG.info(getName() + " thread finished"); FileChangeWatcher.this.setState(FileChangeWatcher.State.STOPPED); } }
/** * Tells the background thread to start. Does not wait for it to be running. * Calling this method more than once has no effect. */ public void start() { if (!compareAndSetState(State.NEW, State.STARTING)) { // If previous state was not NEW, start() has already been called. return; } this.watcherThread.start(); }
/** * Tells the background thread to stop. Does not wait for it to exit. */ public void stop() { if (compareAndSetState( new State[]{State.RUNNING, State.STARTING}, State.STOPPING)) { watcherThread.interrupt(); } }