package marvellousworks.practicalpattern.concept.unittest;
import java.nio.file.WatchEvent.Kind;
/**
* 文件系統事件類型
* @author wangxiang
*
*/
public final class FileSystemEventArgs {
private final String fileName;
private final Kind<?> kind;
public FileSystemEventArgs(String fileName, Kind<?> kind){
this.fileName = fileName;
this.kind = kind;
}
/**
* 文件的路徑
*/
public String getFileName(){return fileName;}
/**
* 操做類型:變動、建立、刪除
*/
@SuppressWarnings("rawtypes")
public Kind getKind(){return kind;}
}
package marvellousworks.practicalpattern.concept.unittest;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.Observable;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import static java.nio.file.StandardWatchEventKinds.*;
/**
* 監控一個目錄內文件的更新、建立和刪除事件(不包括子目錄)
*
* 對於http://download.oracle.com/javase/tutorial/essential/io/notification.html進行了改造
* 使其更接近.NET的DirectoryWatcher使用習慣
*
* 因爲java沒有相似.NET源生的事件機制
* 所以實現上採用了Java SE自帶的Observer/Observable對象對外拋出「假」事件
*
* 適於Java SE 7
*
* @author wangxiang
*
*/
public class DirectoryWatcher extends Observable{
private WatchService watcher;
private Path path;
private WatchKey key;
private Executor executor = Executors.newSingleThreadExecutor();
FutureTask<Integer> task = new FutureTask<Integer>(
new Callable<Integer>(){
public Integer call() throws InterruptedException{
processEvents();
return Integer.valueOf(0);}});
@SuppressWarnings("unchecked")
static <T> WatchEvent<T> cast(WatchEvent<?> event) {
return (WatchEvent<T>) event;
}
public DirectoryWatcher(String dir) throws IOException {
watcher = FileSystems.getDefault().newWatchService();
path = Paths.get(dir);
// 監控目錄內文件的更新、建立和刪除事件
key = path.register(watcher, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE);
}
/**
* 啓動監控過程
*/
public void execute(){
// 經過線程池啓動一個額外的線程加載Watching過程
executor.execute(task);
}
/**
* 關閉後的對象沒法從新啓動
* @throws IOException
*/
public void shutdown() throws IOException {
watcher.close();
executor = null;
}
/**
* 監控文件系統事件
*/
void processEvents() {
while (true) {
// 等待直到得到事件信號
WatchKey signal;
try {
signal = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event : signal.pollEvents()) {
Kind<?> kind = event.kind();
// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}
// Context for directory entry event is the file name of entry
WatchEvent<Path> ev = cast(event);
Path name = ev.context(); notifiy(name.getFileName().toString(), kind); } // 爲監控下一個通知作準備 key.reset(); } } /** * 通知外部各個Observer目錄有新的事件更新 */ void notifiy(String fileName, Kind<?> kind){ // 標註目錄已經被作了更改 setChanged(); // 主動通知各個觀察者目標對象狀態的變動 // 這裏採用的是觀察者模式的「推」方式 notifyObservers(new FileSystemEventArgs(fileName, kind)); }}
package marvellousworks.practicalpattern.concept.unittest;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import org.junit.Test;
import static java.nio.file.StandardWatchEventKinds.*;
public class DirectoryWatcherFixture {
private static final String DIR_PATH =System.getProperty("user.dir");
private static final File DIR = new File(DIR_PATH);
private static final String SUFFIX = ".txt";
private static final String PREFIX = "test";
private static final int ADD_TIMES = 3;
/**
* 觀察者
* @author wangxiang
*
*/
public class Logger implements Observer{
@Override
public void update(Observable observable, Object eventArgs) {
FileSystemEventArgs args = (FileSystemEventArgs) eventArgs;
System.out.printf("%s has been %s\n", args.getFileName(), args.getKind());
assertTrue(args.getFileName().startsWith(PREFIX));
assertEquals(ENTRY_CREATE, args.getKind());
}
}
@Test
public void testWatchFile() throws IOException, InterruptedException{
DirectoryWatcher watcher = new DirectoryWatcher(DIR_PATH);
Logger l1 = new Logger();
watcher.addObserver(l1);
watcher.execute();
// 建立一系列臨時文件
List<String> files = new ArrayList<>();
for(int i=0; i<ADD_TIMES; i++){
files.add(File.createTempFile(PREFIX, SUFFIX, DIR).toString());
}
// 延遲等待後臺任務的執行
Thread.sleep(4000);
watcher.shutdown();
System.out.println("finished");
}
}
test5769907807190550725.txt has been ENTRY_CREATE
test4657672246246330348.txt has been ENTRY_CREATE
test1823102943601166149.txt has been ENTRY_CREATEfinished