/**
* Copyright 2014 Yahoo! Inc. Licensed under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
* or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.yahoo.sql4d.indexeragent.util;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A generic file change observer. Looks at the file extension first and then the event.
* @author srikalyan
*/
public abstract class FileSniffer {
private static final Logger log = LoggerFactory.getLogger(FileSniffer.class);
private final Path sniffPath;
private final String fileExtension;
private WatchService watcher;
private final AtomicBoolean stopSniff = new AtomicBoolean(false);
private Thread snifferThread;
public FileSniffer(String pathToSniff, String fileExtension) {
sniffPath = Paths.get(pathToSniff);
this.fileExtension = fileExtension;
try {
watcher = sniffPath.getFileSystem().newWatchService();
sniffPath.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
} catch (IOException e) {
log.error("Error: {}", e);
}
}
public void startSniffing() {
snifferThread = new Thread(new Runnable() {
@Override
public void run() {
while (!stopSniff.get()) {
try {
// Could have done take() instead of poll() but that blocks indefinitely.
WatchKey watckKey = watcher.poll(3, TimeUnit.SECONDS);
if (stopSniff.get()) {
break;
}
if (watckKey == null) {
continue;
}
for (WatchEvent<?> event : watckKey.pollEvents()) {
log.info("Event Kind : {}", event.kind());
if (event.kind() == OVERFLOW) {// If event is lost/discarded.
continue;
}
log.info("Event: {}", event.context().toString());
if (event.context().toString().endsWith(fileExtension) && (event.context() instanceof Path)) {
Path fullPath = Paths.get(sniffPath.toString(), ((Path)event.context()).getFileName().toString());
if (event.kind() == ENTRY_CREATE) {
log.info("New file found: {}", event.context().toString());
onCreate(fullPath);
}
if (event.kind() == ENTRY_DELETE) {
log.info("File {} deleted.", event.context().toString());
onDelete(fullPath);
}
if (event.kind() == ENTRY_MODIFY) {
log.info("File {} modified.", event.context().toString());
onModify(fullPath);
}
}
}
if (!watckKey.reset()) {
log.error("Watch key has become invalid !!");
stopSniff.set(true);
}
} catch (InterruptedException ex) {
log.error("Exception while sniffing {}", ex);
stopSniff.set(true);
}
}
}
});
snifferThread.start();
}
public void stopSniffing() {
stopSniff.set(true);
}
public abstract void onCreate(Path path);
public abstract void onDelete(Path path);
public abstract void onModify(Path path);
}