对文件及文件夹进行修改变更监测有很广泛的应用,例如:
- 通知配置文件的改变
- 跟踪某些关键的系统文件的变化
- 监控某个分区磁盘的整体使用情况
- 系统崩溃时进行自动清理
- 自动触发备份进程
- 向服务器上传文件结束时发出通知
下面给出Java的两种实现,源码可以在GitHub上找到 FileMonitor
JDK1.6及之前版本: 基于Timer实现
两个关键类:
- java.util.Timer
- java.util.TimerTask
Timertask是由Timer执行的实际任务,实现了Rannable接口。通过重写run()方法来指定具体任务细节。
Timer工作原理:
Timer是用于调度一次性执行或重复执行的工具类。通过TaskQueue保存需要调度的TimerTask,当某个Task被废弃时(一次性任务结束或TimerTask.cancel()),将其从该队列中移除。
Timer类维护一个后台线程(守护线程或用户线程,取决于如何创建Timer对象),该线程通常称为Timer任务执行线程。在TimerThread的mainLoop()中依据各个TimerTask的状态和调度时间设定,决定执行或移除TimerTask。
TimerTask应设计为执行不占用太长时间,否则同一个Timer队列中其他的TimerTask的执行将会延迟。
基于Timer的FileMonitor的实现:
FileChangeMonitor本身是一个单例。fileObservers由Collections.synchronizedMap()初始化,保证在该map上的每一个原子操作都将被同步。在其addObserver方法中为每一个fileChangeObserver创建一个FileChangeTask,将其加入fileObservers中。FileChangeTask扩展了TimerTask,由Timer调度执行。
public void addObserver(FileChangeObserver observer, String filename, long delay) throws FileNotFoundException {
TimerTask task = new FileChangeTask(observer , filename );
List<TimerTask> tasks = fileObservers.get(filename );
if(tasks ==null){
tasks = new ArrayList<TimerTask>();
}
tasks.add( task);
fileObservers.put(filename , tasks );
timer.schedule( task, delay, delay);
}
在FileChangeTask的run()函数中,通过比对时间戳来判断文件是否修改,若发生改动,则通知其Observer进行相应处理。
public void run() { try { long newLastModified = file.lastModified(); if (newLastModified > lastModified )[ ] { lastModified = newLastModified ;[ ] observer.fileChanged( file.getPath()); } } catch (Exception e ) { System. err.println(e .getMessage()); } }测试用例FileMonitorTest中为sample1.txt添加了consoleObserver和emailObserver,为sample2.txt添加了consoleObserver。然后对这两个文件分别进行修改。
package com.cwind.file; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import org.junit.After; import org.junit.Before; import org.junit.Test; public class FileMonitorTest { File sampleFile1, sampleFile2 ; FileChangeMonitor monitor; ConsoleFileChangeObserver consoleObserver; EmailFileChangeObserver emailObserver; @Before public void setUp() throws Exception { sampleFile1 = new File("sample1.txt" ); sampleFile2 = new File("sample2.txt" ); monitor = FileChangeMonitor. getInstance(); consoleObserver = new ConsoleFileChangeObserver(); emailObserver = new EmailFileChangeObserver("billchen01@163.com"); } @After public void tearDown() throws Exception { } @Test public void testMonitorSampleFile() throws InterruptedException, IOException{ monitor.addObserver( consoleObserver, sampleFile1 .getPath(), FileChangeMonitor.DELAY_TIME ); monitor.addObserver( emailObserver, sampleFile1 .getPath(), FileChangeMonitor.DELAY_TIME ); monitor.addObserver( consoleObserver, sampleFile2 .getPath(), FileChangeMonitor.DELAY_TIME ); FileOutputStream fos1 = new FileOutputStream(sampleFile1 ); FileOutputStream fos2 = new FileOutputStream(sampleFile2 ); fos1.write(0); fos2.write(0); fos1.flush(); fos2.flush(); fos1.close(); fos2.close(); Thread. sleep(3000); } }输出结果如下:
Console: File sample1.txt is changed, will print warning message to console. File sample1.txt is changed, will send email to billchen01@163.com. Console: File sample2.txt is changed, will print warning message to console.JDK 1.7 及之后版本:基于WatchService实现
Java 7 的新IO - NIO.2提供了一组新的类和方法,主要存在于java.nio包内。它完全取代了java.io.File与文件系统的交互,并提供了新的异步处理类,无需手动配置线程池和其他底层并发控制,便可在后台线程中执行文件和网络IO操作。
其中Path是新文件IO的基石。为与之前版本兼容,java.io.File类中新增了toPath()方法,Path类中提供了toFile()方法。
Watch Service API可用于将指定目录注册到监视服务上。注册时须指定事件类型,如文件创建、修改、删除等。相关类图如下:
WatchService是监视服务接口,在不同系统上有不同的实现类。实现了Watchable接口的对象方可注册监视服务,java.nio.file.Path实现了此接口。WatchKey表示Watchable对象和WatchService的关联关系,在注册时被创建。注册完成后,WatchKey将被置为'ready'状态,直到下列三种情况之一发生:
1. WatchKey.cancel()被调用
2. 被监控的目录不存在或不可访问
3. WatchService对象被关闭
当文件改动发生时,WatchKey的状态将会被置为"signaled"然后被放入待处理队列中。WatchService提供了三种从队列中获取WatchKeys的方式:
1. poll - 返回队列中的一个key。如果没有可用的key,将立即返回null。
2. poll(long, TimeUnit) - 如果队列中存在可用的key则将之返回,否则在参数预置的时间内等待可用的key。TimeUnit用来指定前一个参数表示的时间是纳秒、毫秒或是其他的时间单位。
例子:final WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);将会等待1分钟
3. take - 方法将会等待直到可用的key被返回。
获取WatchKey后进行处理:
1. 通过WatchKey.pollEvents()函数得到WatchEvents列表。
2. 对于每一个WatchEvent,可以通过kind()函数获得其改动类型。
3. 通过WatchEvent.context()函数得到发生该事件的文件名
4. 当该key的所有事件处理完成后,需要调用WatchKey.reset()方法把该key重置为ready状态。若不重置,该key将无法接收后续的改动。若reset返回false,表示该WatchKey不再合法,主循环可以退出。
示例程序:
package com.cwind.file; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchEvent; import java.nio.file.WatchKey; import java.nio.file.WatchService; public class WatchServerTest { public static void main(String[] args) throws InterruptedException, IOException { WatchService watchService = FileSystems.getDefault().newWatchService(); final Path path = Paths.get( "."); final WatchKey watchKey = path.register( watchService, StandardWatchEventKinds.ENTRY_MODIFY , StandardWatchEventKinds. ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE ); boolean fileNotChanged = true; int count = 0; while (fileNotChanged ) { final WatchKey wk = watchService .take(); System. out.println("Loop count: " + count ); for (WatchEvent<?> event : wk .pollEvents()) { final Path changed = (Path) event.context(); System. out.println(changed + ", " + event .kind()); if (changed .endsWith("sample1.txt")) { System. out.println("Sample file has changed" ); } } // reset the key boolean valid = wk .reset(); if (!valid ) { System. out.println("Key has been unregisterede" ); } count++; } } }总结,使用WatchService步骤如下:
- 创建WatchService
- 得到待检测目录的Path
- 将目录登记到变化监测名单中
- 执行WatchService的take()方法,直到WatchKey到来。
- 得到WatchKey后遍历WatchEvent进行检测
- 重置key准备下一个事件,继续等待
大多数文件系统实现包含了文件更改通知的本地支持,Watch Service API正是利用了文件系统的这种机制。若文件系统并不支持变更通知机制,Watch Service仍然会轮询文件系统,等待事件产生。
References:
相关推荐
本项目提供了一种实现方式,通过名为"src.rar"的压缩包,我们可以看到其中包含两个核心文件:ZJPFileListener.java和ZJPFileMonitor.java。这两个文件分别代表了文件监听器和文件监控器的实现,用于实时监控指定...
总之,Flink-CDC是实现实时数据源监控和变更数据捕获的强大工具,其高效、稳定且具有广泛生态支持的特点,使得它在大数据实时处理领域具有广泛的应用前景。对于需要实现实时数据同步和ETL的企业来说,Flink-CDC是一...
在Java编程中,实时读取日志和实现进度条是两个重要的功能,广泛应用于系统监控、调试和用户界面的交互性提升。以下是对这两个知识点的详细讲解。 首先,让我们来看看如何在Java中实时读取日志。日志是程序运行过程...
Java项目实现热更源码基于Java Agent是一种在不重启服务的情况下更新代码的技术,这对于持续运行的服务尤其重要,因为它可以减少停机时间和维护成本。本文将深入探讨如何利用Java Agent实现热更新,以及涉及的关键...
Java中的WatchService是Java 7中引入的一种监控文件系统目录的变化的机制,可以用来监控文件的创建、删除、修改等事件。下面是使用WatchService监控文件内容变化的示例。 WatchService的使用过程可以分为三步:...
首先,要实现数据库监听,我们需要理解两种主要的技术手段:JDBC的Statement监听和数据库触发器。 1. **JDBC的Statement监听**: JDBC(Java Database Connectivity)是Java访问数据库的标准API。通过注册`...
在Java中实现数据库监听通常依赖于以下几种技术: 1. **数据库触发器**:可以在数据库层面定义,在特定的数据库事件(如INSERT、UPDATE、DELETE)发生时自动执行预定义的SQL语句。这是实现细粒度监听的有效方式。 ...
1. **定时任务**:使用Java的ScheduledExecutorService或Quartz等库,定期轮询两个数据库,检查是否有新的增删改查操作,如果有,则在另一数据库中执行相应的操作。 2. **监听事件**:MySQL支持触发器和binlog日志...
- 文件系统变更监控; - 数据同步; - 安全事件记录。 #### HTTPS流量监控Hyperfox - **简介**:Hyperfox是一款用于HTTPS流量监控的工具。 - **最新版本**:Hyperfox,此版本发布于两年前。 - **功能**: - ...
提供的"Demo"很可能是用某种编程语言(如Python、Java、C#等)实现的一个示例程序,用于演示如何使用Dir Watcher库来监控文件夹变化。它可能包括以下部分: 1. 初始化:设置要监控的文件夹路径,并配置感兴趣的事件...
在实际的应用场景中,例如文本编辑器、图像处理软件,甚至是版本控制系统,这两种技术都有广泛的应用。文件拖放打开使得用户能快速预览或编辑文件,而文件监视则确保了用户对文件状态的实时感知,降低了数据丢失的...
Java数据实时同步系统是一种用于在不同数据库之间进行实时数据交换的技术解决方案。这种系统的主要目标是确保数据的一致性和完整性,特别是在分布式环境中,当数据需要在远程数据库和本地数据库之间频繁流动时。以下...
在Java中,有两种主要的解析方式: 1. **DOM解析**:Document Object Model(DOM)将整个XML文档加载到内存中,形成一棵树形结构,便于遍历和修改。Java中的`javax.xml.parsers.DocumentBuilderFactory`和`org.w3c....
通过具体示例来展示如何使用变更事件和分布对象来监控和响应序列数据的变化。 - **Distribution**:用于统计序列数据中的某些特性,如碱基频率。 - **ChangeListener**:用于处理序列变化引起的事件。 ##### 6. ...
9. **文件系统事件(WatchService)**:Java 7引入了WatchService API,允许程序监控文件系统的变更事件,如文件创建、删除、修改等。 10. **案例分析**:书中可能会通过实际的案例来展示如何使用NIO构建高性能的...
文件系统的变更,如文件创建、修改或删除,也可以用监听者模式处理。可以定义一个`FileSystemChangeEvent`接口,`FileSystemObserver`负责监听文件系统的变化,如文件系统的`FileWatcher`类实现`Observable`,在...
本文主要关注两种技术:JNotify和EHCache,它们分别用于文件系统监控和内存缓存管理。 JNotify是一个Java库,通过JNI(Java Native Interface)技术实现了跨平台的文件系统事件监控。它允许Java代码监听指定文件或...
标签“java”和“jsp”是两种重要的编程技术和Web开发框架。Java是一种广泛使用的面向对象的编程语言,具有跨平台的特性,适用于开发各种类型的应用程序,包括服务器端应用。而jsp是Java Web开发的一种技术,用于...
1. **Java编程基础**:Java是一种跨平台的面向对象的编程语言,用于实现系统的后端逻辑。开发者需要掌握Java语法、类与对象、异常处理、多线程、IO流等基础知识。 2. **Swing或JavaFX图形界面**:为了创建用户界面...