Watching file system in java
Ứng dụng
- Được áp dụng trong một số hệ thống cần chức năng nhận biết sự thay đổi khi thêm các directory, file trong một directory cụ thể.
- Một hệ thống monitoring những thay đổi: create, edit, delete. Và xử lý với những sự thay đổi đó.
WatchService
Là một API của Java thực hiện monitor sự thay đổi của 1 directory cụ thể!
Các xử lý chính của API:
- Tạo một WatchService.
- đăng ký các directory muốn xem sự thay đổi, và đăng ký với WatchServer. Đăng ký thêm các hành vi để thông báo. Các hành vi bao gồm:
ENTRY_CREATE
,ENTRY_DELETE
,ENTRY_MODIFY
. - Thực hiện một loop vô hạn để lắng nghe các hành vi trên các directory đã đăng ký. Các hành vi được được sinh ra được gọi là Key, và được đưa vào watcher queue.
- Lấy các Key từ trong watcher queue và xử lý cần thiết với sự thay đổi. Thông tin về sự thay đổi, file hoặc folder thay đổi cũng được lưu trong object Key.
- Reset Key và tiếp tục chờ hành vi tiếp.
- Close Service.
Code Sample
- Tạo một WatchService
WatchService watcher = FileSystems.getDefault().newWatchService();
- Đăng ký các action để monitor trên một Directory: ví dụ dưới đăng ký 3 action:
Path dir = Paths.get("/tmp/testWatcher");
WatchKey key = dir.register(watcher,ENTRY_CREATE,ENTRY_DELETE,ENTRY_MODIFY);
- Thực hiện loop để xử lý khi nhận các action
// Start the infinite polling loop
WatchKey key = null;
while (true) {
key = service.take();
// Dequeueing events
Kind<?> kind = null;
for (WatchEvent<?> watchEvent : key.pollEvents()) {
// Get the type of the event
kind = watchEvent.kind();
if (OVERFLOW == kind) {
continue; // loop
}
WatchEvent<Path> ev = cast(watchEvent);
Path name = ev.context();
Path child = path.resolve(name);
// print out event
System.out.format("%s: %s\n", watchEvent.kind().name(), child);
if (ENTRY_CREATE == kind) {
// Output
System.out.println("New path Created");
} else if (ENTRY_MODIFY == kind) {
// Output
System.out.println("Modified: ");
} else if (ENTRY_DELETE == kind) {
// Output
System.out.println("Delete: ");
}
}
if (!key.reset()) {
break; // loop
}
}
Kết quả:
526 touch foo.txt
527 vi foo.txt
528 touch bar.txt
529 rm bar.txt
530 mkdir testFolder
531 cd testFolder/
532 touch foo2.txt
533 vi foo2.txt
Watching path: /tmp/testWatcher
ENTRY_CREATE: /tmp/testWatcher/foo.txt
New path Created
ENTRY_MODIFY: /tmp/testWatcher/foo.txt
Modified:
ENTRY_CREATE: /tmp/testWatcher/bar.txt
New path Created
ENTRY_DELETE: /tmp/testWatcher/bar.txt
Delete:
ENTRY_CREATE: /tmp/testWatcher/testFolder
New path Created
ENTRY_MODIFY: /tmp/testWatcher/testFolder
Modified:
ENTRY_MODIFY: /tmp/testWatcher/testFolder
Modified:
ENTRY_MODIFY: /tmp/testWatcher/testFolder
Modified:
Đánh giá.
- Với folder:
/tmp/testWatcher
thì mọi hành vi trong folder này như create, modify, delete đều được WatchService nhận biết và đưa ra log. - Với folder con thì các action không nhận biết được.
- Vậy để có thể biết được sự thay đổi của các folder con bên trong thì các folder con cũng cần đăng ký với watcher như ở bước 2. Vì vậy cần duyệt folder. Để duyệt folder thì dùng thêm Files.walkFileTree
Walking the File Tree
- Là chức năng duyệt một lượt tất cả các file, folder trong hệ thống bên trong một folder cụ thể.
- Thực thi một interface:
java.nio.file.FileVisitor
- Có 4 phương thức có thể ghi đè lên FileVisitor:
- preVisitDirectory: thực hiện trước khi visit Directory
- postVisitDirectory: thực hiện khi đã visit Directory
- visitFile: Thực hiện khi đã visit File
- visitFileFailed: Thực hiện xử lý khi không thể visit File
Sample:
File dir = new File("/tmp/testWatcher");
Path start = dir.toPath();
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
System.out.println("PreVisit Directory:" + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException attrs)
throws IOException
{
System.out.println("Visited Directory:" + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path dir, BasicFileAttributes attrs)
throws IOException
{
if(attrs.isRegularFile()) {
System.out.println("Visit File:" + dir);
}
return FileVisitResult.CONTINUE;
}
});
Kết quả:
- Folder test tree
$ tree -a testWatcher
testWatcher
├── .pm
├── foo.txt
└── testFolder
└── foo2.txt
- Chương trình
PreVisit Directory:/tmp/testWatcher
Visit File:/tmp/testWatcher/.pm
Visit File:/tmp/testWatcher/foo.txt
PreVisit Directory:/tmp/testWatcher/testFolder
Visit File:/tmp/testWatcher/testFolder/foo2.txt
Visited Directory:/tmp/testWatcher/testFolder
Visited Directory:/tmp/testWatcher
Đánh giá:
- Như vậy
Files.walkFileTree
có thể duyệt được tất cả các file, folder trong 1 folder cố định. - **Việc kết hợp giữa Files.walkFileTree và WatchService có thể tạo ra một ứng dụng có thể monitor toàn bộ một folder và các folder con, cháu... **
Code & Result
Ý tưởng của việc kết hợp:
- Với một folder chỉ định, dùng
Files.walkFileTree
để đăng ký các folder con, cháu... của Folder chỉ định. - Đăng ký các folder với WatchService
- WatchService lắng nghe các action trên các folder.
- Khi có một folder mới được tạo, thì dùng
Files.walkFileTree
đăng ký folder con của folder đó với watchService.
Code Sample:
Kết quả:
549 touch bar.txt
550 mkdir testFolder2/testFolder22
551 mkdir -p testFolder2/testFolder22
552 cd testFolder2
553 touch foo3.txt
554 vi foo3.txt
555 cd testFolder22/
556 touch foo33.txt
557 touch bar33.txt
558 rm -rf bar33.txt
Scanning /tmp/testWatcher ...
Done.
ENTRY_CREATE: /tmp/testWatcher/bar.txt
ENTRY_CREATE: /tmp/testWatcher/testFolder2
register: /tmp/testWatcher/testFolder2
register: /tmp/testWatcher/testFolder2/testFolder22
ENTRY_MODIFY: /tmp/testWatcher/testFolder2
ENTRY_CREATE: /tmp/testWatcher/testFolder2/foo3.txt
ENTRY_MODIFY: /tmp/testWatcher/testFolder2
ENTRY_MODIFY: /tmp/testWatcher/testFolder2/foo3.txt
ENTRY_MODIFY: /tmp/testWatcher/testFolder2/testFolder22
ENTRY_CREATE: /tmp/testWatcher/testFolder2/testFolder22/foo33.txt
ENTRY_MODIFY: /tmp/testWatcher/testFolder2/testFolder22
ENTRY_CREATE: /tmp/testWatcher/testFolder2/testFolder22/bar33.txt
ENTRY_MODIFY: /tmp/testWatcher/testFolder2/testFolder22
ENTRY_DELETE: /tmp/testWatcher/testFolder2/testFolder22/bar33.txt