Observer - IO (File Monitor)
1. 概述
有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
2. 解决的问题
将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
3. 模式中的角色
3.1 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
3.2 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
3.3 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
3.4 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
这里选取Apache Commons IO的monitor来分析观察者模式。
Observer使用
// File Monitoring
// Create a new observer for the folder and add a listener
// that will handle the events in a specific directory and take action.
File parentDir = FileUtils.getFile(PARENT_DIR);
FileAlterationObserver observer = new FileAlterationObserver(parentDir);
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileCreate(File file) {
System.out.println("File created: " + file.getName());
}
@Override
public void onFileDelete(File file) {
System.out.println("File deleted: " + file.getName());
}
@Override
public void onDirectoryCreate(File dir) {
System.out.println("Directory created: " + dir.getName());
}
@Override
public void onDirectoryDelete(File dir) {
System.out.println("Directory deleted: " + dir.getName());
}
});
// Add a monior that will check for events every x ms,
// and attach all the different observers that we want.
FileAlterationMonitor monitor = new FileAlterationMonitor(500, observer);
try {
monitor.start();
// After we attached the monitor, we can create some files and directories
// and see what happens!
File newDir = new File(NEW_DIR);
File newFile = new File(NEW_FILE);
newDir.mkdirs();
newFile.createNewFile();
Thread.sleep(1000);
FileDeleteStrategy.NORMAL.delete(newDir);
FileDeleteStrategy.NORMAL.delete(newFile);
Thread.sleep(1000);
monitor.stop();
} catch (IOException e) {
//..
文件变更Observer 变量声明
public class FileAlterationObserver implements Serializable {
private static final long serialVersionUID = 1185122225658782848L;
private final List<FileAlterationListener> listeners = new CopyOnWriteArrayList<FileAlterationListener>();
private final FileEntry rootEntry;
private final FileFilter fileFilter;
private final Comparator<File> comparator;
//...
}
CopyOnWriteArrayList是ArrayList 的一个线程安全的变体,其中所有可变操作(add、set等等)都是通过对底层数组进行一次新的复制来实现的。其 add(E) 和remove(int index)都是对新的数组进行修改和新增。所以在多线程操作时不会出现java.util.ConcurrentModificationException错误。
参考:
http://my.oschina.net/jielucky/blog/167198
判断变更的逻辑 checkAndNotify方法:
final File rootFile = rootEntry.getFile();
if (rootFile.exists()) {
checkAndNotify(rootEntry, rootEntry.getChildren(), listFiles(rootFile));
} else if (rootEntry.isExists()) {
checkAndNotify(rootEntry, rootEntry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
//FileUtils.EMPTY_FILE_ARRAY=File[0];
} else {
// Didn't exist and still doesn't
} private void checkAndNotify(final FileEntry parent, final FileEntry[] previous, final File[] files) {
int c = 0;
final FileEntry[] current = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_ENTRIES;
for (final FileEntry entry : previous) {
while (c < files.length && comparator.compare(entry.getFile(), files[c]) > 0) {
current[c] = createFileEntry(parent, files[c]);
doCreate(current[c]);
c++;
}
if (c < files.length && comparator.compare(entry.getFile(), files[c]) == 0) {
doMatch(entry, files[c]);
checkAndNotify(entry, entry.getChildren(), listFiles(files[c]));
current[c] = entry;
c++;
} else {
checkAndNotify(entry, entry.getChildren(), FileUtils.EMPTY_FILE_ARRAY);
doDelete(entry);
}
}
for (; c < files.length; c++) {
current[c] = createFileEntry(parent, files[c]);
doCreate(current[c]);
}
parent.setChildren(current);
}
//example
private void doMatch(final FileEntry entry, final File file) {
if (entry.refresh(file)) {
for (final FileAlterationListener listener : listeners) {
if (entry.isDirectory()) {
listener.onDirectoryChange(file);
} else {
listener.onFileChange(file);
}
}
}
}
观察类实现的ListFiles 方法:
private File[] listFiles(final File file) {
File[] children = null;
if (file.isDirectory()) {
children = fileFilter == null ? file.listFiles() : file.listFiles(fileFilter);
}
if (children == null) {
children = FileUtils.EMPTY_FILE_ARRAY;
}
if (comparator != null && children.length > ) {
Arrays.sort(children, comparator);
}
return children;
}
Monitor 类:
public synchronized void start() throws Exception {
if (running) {
throw new IllegalStateException("Monitor is already running");
}
for (final FileAlterationObserver observer : observers) {
observer.initialize();
}
running = true;
if (threadFactory != null) {
thread = threadFactory.newThread(this);
} else {
thread = new Thread(this);
}
thread.start();
}
public synchronized void stop(final long stopInterval) throws Exception {
if (running == false) {
throw new IllegalStateException("Monitor is not running");
}
running = false;
try {
thread.join(stopInterval);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
}
for (final FileAlterationObserver observer : observers) {
observer.destroy();
}
}
public void run() {
while (running) {
for (final FileAlterationObserver observer : observers) {
observer.checkAndNotify();
}
if (!running) {
break;
}
try {
Thread.sleep(interval);
} catch (final InterruptedException ignored) {
}
}
}
我们使用org.apache.commons.io.monitor包下的类创建了一个处理器来监听一些特定的事件(在上面的例子中就是我们对文件或目录所做的所有操作事件),为了获得这些信息,我们需要做以下几步操作:
1、创建一个File对象,这个对象指向我们需要监听变化的目录。
2、创建一个FileAlterationObserver对象,这个对象会观察这些变化。
3、通过调用addListener()方法,为observer对象添加一个 FileAlterationListenerAdaptor对象。你可以通过很多种方式来创建一个适配器,在我们的例子中我们使用内部类的方式进行创建并且只实现其中的一部分方法(只需要实现我们例子中需要用的方法即可)。
4、创建一个FileAlterationMonitor 对象,将已经创建好的observer对象添加其中并且传入时间间隔参数(单位是毫秒)。
5、调用start()方法即可开启监视器,如果你想停止监视器,调用stop()方法即可。
Observer - IO (File Monitor)的更多相关文章
- java.io.NotSerializableException: test.io.file.Student
java.io.NotSerializableException: test.io.file.Student at java.io.ObjectOutputStream.writeObject0 ...
- IO:File类(java.io.File)
public class File extends Object implements Serializable, Comparable<File> 构造方法: public File(S ...
- java.io.file
package cn.edu.tongji.cims.wade.system; import java.io.*; public class FileOperate { pub ...
- java获取指定路径下的指定文件/java.io.File.listFiles(FilenameFilter filter)
java.io.File.listFiles(FilenameFilter filter) 返回抽象路径名数组,表示在目录中此抽象路径名表示,满足指定过滤器的文件和目录. 声明 以下是java.io. ...
- 【java IO File】统计项目代码总共多少行
统计项目代码总共有多少行 思想: 1.首先将不需要迭代的文件夹,保存在集合中,不满足的就是需要迭代的文件夹 2.将需要进行统计行数的代码文件保存在集合中,满足的就是需要计算文件行数的文件 3.迭代方法 ...
- System.IO.File.Create 不会自动释放,一定要Dispose
这样会导致W3P进程一直占用这个文件 System.IO.File.Create(HttpContext.Current.Server.MapPath(strName)) 最好加上Dispose Sy ...
- IIS目录下文件共享后System.IO.File.Exists返回false
场景:在iis目录下,因为特殊需要共享一个文件夹,给到其他的技术人员访问,突然发现小小的操作,搞“大”了,使用 string path = Server.MapPath("~/file/te ...
- java.io.File类
java.io.File类 1.凡是与输入.输出相关的类.接口等都定义在java.io包下 2.File是一个类.能够有构造器创建其对象.此对象相应着一个文件(.txt .avi .doc .ppt ...
- 详解C#中System.IO.File类和System.IO.FileInfo类的用法
System.IO.File类和System.IO.FileInfo类主要提供有关文件的各种操作,在使用时需要引用System.IO命名空间.下面通过程序实例来介绍其主要属性和方法. (1) 文件打开 ...
随机推荐
- eBay_Relist(退换刊登费)
如果物品首次刊登结束时没有人中标,或是买家并没有付款完成交易,那么卖家以通过重新刊登的方法来再次销售.如果该物品在第二次刊登时成功售出,且满足eBay的重新刊登退费条件,那么eBay便会退还重新刊登的 ...
- Qt 串口学习3
1.怎样将选择波特率 1)使用if else 的方法 if(ui->BaudRate->currentText()==tr("9600")) //根据组合框 ...
- spark共享变量
boradcast例子代码: scala版本 spark共享变量之Accumulator 例子代码: scala版本
- Android概述(思维导图)
- Optimize Managed Code For Multi-Core Machines
Parallel Performance Optimize Managed Code For Multi-Core Machines Daan Leijen and Judd Hall This ar ...
- ToDictionary,ToLookup
这个系列我们看看C#中有哪些我们知道,但是又不知道怎么用,又或者懒得去了解的东西,比如这篇我们要介绍的toDictionary 和ToLookup. 从图中我们看到有四个ToXXX的方法,其中ToAr ...
- SqlBulkCopy 批量插入数据库
/// <summary> /// 批量插入 注:DT的tableName为要更新的数据库表名,DT的列名和数据库一致 /// </summary> /// <param ...
- xenserver
Citrix XenServer is the complete server virtualization platform from Citrix. citrix xenserver是来自citr ...
- OpenJudge计算概论-求特殊自然数【枚举法】【将整数转成字符串】
/*======================================================================== 求特殊自然数 总时间限制: 1000ms 内存限制 ...
- Tengine:基于Nginx的衍生版
engine是由淘宝网发起的Web服务器项目.它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性.Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验 ...