通过Java SE 7自带的监控服务(WatchService API)实现类似.NET FileWatcher的功能《模式——工程化实现及扩展》
通过Java SE 7自带的监控服务(WatchService API)实现类似.NET FileWatcher的功能
《模式——工程化实现及扩展》(设计模式Java 版)
?
?
Java SE 7 Tutorial中增加了一个监控目录变更情况的示例,用于介绍其新发布的WatchService API。
?
但对于用惯了.NET FileWatcher的用户而言,如果用于项目我认为它有两个欠缺:
1、应该提供一个独立线程后台运行机制,让这个监控过程自己在后台转,不影响前端处理
2、 Java不像.NET有内置的源生事件机制,不过可以借助它内置的Observer/Observable对象用观察者模式实现准事件
?
下面是把Java SE Tutorial示例中无关内容删除,补充上述两个扩展后的实现,因为这个API比较新,也希望能和大家多多探讨:
?
1、参考.NET定义事件参数对象
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;}
}
?
?2、定义DirectoryWatcher,用于监控某个文件夹,至于如何扩展FileWatcher则可以在这个基础上通过限定文件名称和操作类型的方式扩展
?
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));
????}
}
?
?3、单元测试
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");
????}
}
?
?Console窗口显示的测试内容
?
test5769907807190550725.txt?has?been?ENTRY_CREATE
test4657672246246330348.txt?has?been?ENTRY_CREATE
test1823102943601166149.txt?has?been?ENTRY_CREATE
finished
?