package com.tommy.file.monitor;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
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.File;
import java.io.IOException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.tommy.file.monitor.exception.MonitorException;
import com.tommy.file.monitor.listener.Listener;
/**
* Usage FileMonitor monitor = FileMonitors.newFileMonitor();
* monitor.addWatch(new File("...")); monitor.register(listener);
* monitor.start();
*
* ... monitor.stop();
*
* 注意: monitor.start(), 该方法并不会产生新线程,如果有需要,请在线程里调用monitor.start()
*
* @author Administrator
*
*/
public class FileMonitor {
private static final Log LOG = LogFactory.getLog(FileMonitor.class);
private WatchService watchService;
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private static final int EVENT_RENAME_FIEL_MODIFIER = 1;
private static final int EVENT_RENAME_DIRECTORY_MODIFIER = 2;
private static final int EVENT_NOT_RENAME_MODIFIER = -1;
private static final long WAIT_EVNET_POLL_PERIOD = 200L;
public FileMonitor() {
tryInitWatchService();
}
private final void tryInitWatchService() {
try {
this.watchService = FileSystems.getDefault().newWatchService();
} catch (IOException e) {
throw new MonitorException(e);
} catch (UnsupportedOperationException ue) {
throw new MonitorException.UnsupportMonitorException();
}
}
public boolean register(Listener listener) {
Objects.requireNonNull(listener);
return listeners.add(listener);
}
public boolean unregister(Listener listener) {
Objects.requireNonNull(listener);
return listeners.remove(listener);
}
public void addWatch(File dir) {
addWatch(dir, false);
}
public void addWatch(File dir, boolean recurse) {
validateDir(dir);
notifyListEvent(dir);
Path watchPath = dir.toPath();
if (!recurse) {
register(watchPath, true);
} else {
registerAll(watchPath, true);
}
}
private void notifyListEvent(File dir) {
for (Listener listener : listeners) {
listener.list(dir);
}
}
private void notifySimpleEvent(WatchEvent<Path> watchEvent, WatchKey key) {
try {
Path dirPath = (Path) key.watchable();
File contextFile = dirPath.resolve(watchEvent.context()).toFile();
if (watchEvent.kind() == ENTRY_CREATE) {
for (Listener listener : listeners) {
listener.fileCreated(dirPath.toFile(), contextFile);
}
} else if (watchEvent.kind() == ENTRY_DELETE) {
for (Listener listener : listeners) {
listener.fileDelete(dirPath.toFile(), contextFile);
}
} else if (watchEvent.kind() == ENTRY_MODIFY) {
for (Listener listener : listeners) {
listener.fileModified(dirPath.toFile(), contextFile);
}
} else {
LOG.error("Unknown watchEvent.Kind : "
+ watchEvent.kind().name());
}
} catch (Exception e) {
LOG.error(e.toString(), e);
}
}
private void notifyRenamedEvent(File dir, File oldFile, File newFile) {
try {
for (Listener listener : listeners) {
listener.fileRenamed(dir, oldFile, newFile);
}
} catch (Exception e) {
// if error occurs , log it and go on monitor
LOG.error(e.toString(), e);
}
}
private boolean running = false;
/**
* 开启监控,该方法不会产生新线程
*/
public void start() {
if (isRunning()) {
throw new MonitorException("File Monitor: " + this
+ " has Running.");
}
synchronized (this) {
running = true;
}
processEvents();
}
public synchronized void stop() {
if (!isRunning()) {
throw new MonitorException("File Monitor: " + this
+ " already stoped.");
}
synchronized (this) {
running = false;
}
try {
watchService.close();
} catch (IOException e) {
throw new MonitorException("Stop watchService encounter error. ", e);
}
}
public synchronized boolean isRunning() {
return running;
}
private void processEvents() {
for (;;) {
if (!isRunning()) {
LOG.info("File Monitor stoped.");
break;
}
WatchKey key;
try {
key = watchService.take();
} catch (InterruptedException e) {
if (isRunning()) {
throw new MonitorException(
"FileMonitor interrupted unexpectedly.", e);
} else {
LOG.info("FileMonitor interrupted.");
return;
}
} catch (ClosedWatchServiceException e) {
if (isRunning()) {
throw new MonitorException(
"FileMonitor's watchService closed unexpectedly.",
e);
} else {
LOG.info("FileMonitor closed.");
return;
}
}
// wait events produce fully
try {
TimeUnit.MILLISECONDS.sleep(WAIT_EVNET_POLL_PERIOD);
} catch (InterruptedException ignore) {
// ignore this exception
}
List<WatchEvent<?>> events = key.pollEvents();
if (events == null || events.isEmpty()) {
continue;
}
Path watchPath = (Path) key.watchable();
// remove overflow event
filterEvents(events);
/**
* analyse event
*/
int eventModifier = getEventModifier(events, key);
if (eventModifier == EVENT_RENAME_FIEL_MODIFIER
|| eventModifier == EVENT_RENAME_DIRECTORY_MODIFIER) {
File oldFile = watchPath
.resolve((Path) events.get(0).context()).toFile();
File newFile = watchPath
.resolve((Path) events.get(1).context()).toFile();
File dir = watchPath.toFile();
if (events.get(1).kind() == ENTRY_CREATE) {
Path child = watchPath.resolve((Path) events.get(1)
.context());
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
registerAll(child, false);
}
}
notifyRenamedEvent(dir, oldFile, newFile);
} else {
for (WatchEvent<?> watchEvent : events) {
if (watchEvent.kind() == StandardWatchEventKinds.OVERFLOW) {
continue;
}
WatchEvent<Path> ev = cast(watchEvent);
Path name = ev.context();
if (ev.kind() == ENTRY_CREATE) {
Path child = watchPath.resolve(name);
// recurse all or just it self?
if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
registerAll(child, false);
}
}
notifySimpleEvent(ev, key);
}
}
boolean valid = key.reset();
if (!valid) {
LOG.info("Path '" + key.watchable() + "' not monitor.");
}
}
}
private void filterEvents(List<WatchEvent<?>> events) {
Iterator<WatchEvent<?>> it = events.iterator();
while (it.hasNext()) {
if (it.next().kind() == StandardWatchEventKinds.OVERFLOW) {
it.remove();
}
}
}
private int getEventModifier(List<WatchEvent<?>> events, WatchKey key) {
int len = events.size();
if (len != 2 && len != 3) {
return EVENT_NOT_RENAME_MODIFIER;
}
WatchEvent<Path> deleteEvent = cast(events.get(0));
WatchEvent<Path> createEvent = cast(events.get(1));
WatchEvent<Path> modifyEvent = null;
if (len == 3) {
modifyEvent = cast(events.get(2));
}
// cr
评论5
最新资源