GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Java Core Java I/O Giới thiệu WatchService API trong Java

Giới thiệu WatchService API trong Java

Đăng vào 30/07/2018 . Được đăng bởi GP Coder . 4835 Lượt xem . Toàn màn hình

Nội dung

  • 1 Giới thiệu
  • 2 Các phương thức được cung cấp bởi Watch Service
  • 3 Các loại sự kiện được đăng ký với Watch Service
  • 4 Các bước để sử dụng Watch Service
  • 5 Ví dụ
  • 6 Khi nào cần sử dụng Watch Service API

Giới thiệu

Khi đang chỉnh sửa một tệp, sử dụng một IDE hay một trình soạn thảo khác, và một hộp thoại xuất hiện để thông báo cho bạn rằng một trong các tệp đang mở đã thay đổi trên hệ thống tệp và cần được tải lại? Hay một trình quản lý file có thể tức thời cập nhật, hiển thị danh sách file khi file được tạo hoặc xóa hoặc sửa đổi. Có bao giờ bạn hỏi: tại sao họ có thể làm được như vậy? họ làm bằng cách nào?

Để thực hiện chức năng này, một chương trình phải có khả năng phát hiện những gì đang xảy ra với thư mục có liên quan trên hệ thống file. Khi có thay đổi, nó sẽ được gọi để xử lý và hiển thị tương ứng.

Java 7 đã thêm một tính năng mới vào gói java.nio.file là Watch Service API. API này cho phép bạn đăng ký một thư mục (hoặc các thư mục) với Watch Service API. Khi đăng ký, bạn cần cho Service biết loại sự kiện nào bạn quan tâm: tạo, xóa hoặc sửa đổi file. Khi Service phát hiện một sự kiện đã đăng ký, nó sẽ được chuyển tiếp đến tiến trình đã đăng ký. Quá trình đăng ký có một thread (hoặc thread pool) dành riêng để giám sát cho bất kỳ sự kiện nào mà nó đã đăng ký. Khi một sự kiện xuất hiện, nó được xử lý khi cần thiết.

Trong phần tiếp theo của bài viết này, chúng ta sẽ cùng tìm hiểu cách sử dụng WatchService trong Java.

Các phương thức được cung cấp bởi Watch Service

  • close() : đóng Watch Service.
  • poll() : truy xuất và xóa key tiếp theo hoặc null nếu không tồn tại bất kỳ key nào.
  • poll(long timeout, TimeUnit unit) : truy xuất và xóa key tiếp theo hoặc chờ đợi nếu cần thiết cho đến thời gian chờ đã chỉ định nếu chưa có.
  • take() : truy xuất và xóa key tiếp theo hoặc đợi nếu không có bất kỳ key nào.

Các loại sự kiện được đăng ký với Watch Service

Các sự kiện được định nghĩa trong class StandardWatchEventKinds:

  • ENTRY_CREATE : một directory đã được tạo.
  • ENTRY_DELETE : một directory đã được xóa.
  • ENTRY_MODIFY : một directory đã được sửa.
  • OVERFLOW : cho biết rằng các sự kiện có thể đã bị mất hoặc bị loại bỏ. Bạn không cần phải đăng ký cho sự kiện OVERFLOW để nhận nó.

Các bước để sử dụng Watch Service

Dưới đây là các bước cơ bản cần thiết để sử dụng WatchService:

  • Tạo một WatchService “watcher” cho hệ thống tập tin.
  • Đối với mỗi thư mục mà chúng ta muốn theo dõi, hãy đăng ký nó với watcher. Khi đăng ký một thư mục, chúng ta cần chỉ định loại sự kiện muốn thông báo. Sự kiện có thể là tạo, xóa hoặc sửa đổi. Chúng ta nhận được một instance WatchKey cho mỗi thư mục đã đăng ký.
  • Thực hiện một vòng lặp vô hạn để chờ các sự kiện đến. Khi một sự kiện xảy ra, key được báo hiệu và được đặt vào hàng đợi (queue) của watcher.
  • Truy xuất key (WatchKey) từ hàng đợi của watcher. Chúng ta có thể lấy tên file từ key.
  • Truy xuất mỗi sự kiện đang chờ xử lý cho key (có thể có nhiều sự kiện) và xử lý khi cần.
  • Đặt lại key và tiếp tục chờ sự kiện. Điều quan trọng là phải đặt lại key sau khi các sự kiện đã được xử lý. Nếu không, key sẽ không nhận được thêm sự kiện. Nếu phương thức reset() trả về false, thư mục không thể truy cập được, vì vậy chúng ta có thể thoát khỏi vòng lặp. Key vẫn còn hiệu lực cho đến khi: nó bị hủy bỏ bằng cách gọi phương cancel() của nó một cách tường minh, hoặc bị hủy bỏ mặc định khi đối tượng không thể truy cập được nữa hoặc bằng cách đóng WatchService.
  • Đóng WatchService: WatchService sẽ thoát khi một trong hai luồng thoát hoặc khi nó được đóng (bằng cách gọi phương thức đóng của nó).

Lưu ý: API Watch Service không cho phép đăng ký file riêng lẻ. Chương trình sẽ ném ra một ngoại lệ java.nio.file.NotDirectoryException, nếu bạn đang cố gắng làm như vậy.

Ví dụ

Theo dõi sự thay đổi của một thư mục

Chương trình bên dưới minh họa việc theo dõi một thư mục khi có thay đổi với các sự kiện như thêm, sửa, xóa các file trong thư mục uploads.


package com.gpcoder.watchservice;

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 DirectoryWatchExample {

	public static void main(String[] args) throws IOException {
		WatchService watcher = FileSystems.getDefault().newWatchService();
		Path dir = Paths.get("C:/uploads");
		dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
				StandardWatchEventKinds.ENTRY_MODIFY);

		System.out.println("Watch Service registered for dir: " + dir.getFileName());

		WatchKey key = null;
		while (true) {
			try {
				// System.out.println("Waiting for key to be signalled...");
				key = watcher.take();
			} catch (InterruptedException ex) {
				System.out.println("InterruptedException: " + ex.getMessage());
				return;
			}

			for (WatchEvent<?> event : key.pollEvents()) {
				// Retrieve the type of event by using the kind() method.
				WatchEvent.Kind<?> kind = event.kind();
				WatchEvent<Path> ev = (WatchEvent<Path>) event;
				Path fileName = ev.context();
				if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
					System.out.printf("A new file %s was created.%n", fileName.getFileName());
				} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
					System.out.printf("A file %s was modified.%n", fileName.getFileName());
				} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
					System.out.printf("A file %s was deleted.%n", fileName.getFileName());
				}
			}

			boolean valid = key.reset();
			if (!valid) {
				break;
			}
		}
	}
}

Thực thi chương trình trên và thêm/ sửa/ xóa một vài file trong thư mục “C:/uploads”, bạn sẽ thấy kết quả như sau:


Watch Service registered for dir: uploads
A new file test.txt was created.
A file test.txt was modified.
A new file image1.png was created.
A file image1.png was modified.
A new file image2.png was created.
A file image2.png was modified.
A file image2.png was deleted.
A file test.txt was modified.
A file test.txt was modified.

Theo dõi sự thay đổi của một thư mục, thư mục con và các tập tin


package com.gpcoder.watchservice;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

public class RecursiveWatchServiceExample {

	private final WatchService watcher;
	private final Map<WatchKey, Path> keys;

	public static void main(String[] args) throws IOException {
		Path dir = Paths.get("C:/uploads");
		new RecursiveWatchServiceExample(dir).processEvents();
	}

	/**
	 * Creates a WatchService and registers the given directory
	 */
	private RecursiveWatchServiceExample(Path dir) throws IOException {
		this.watcher = FileSystems.getDefault().newWatchService();
		this.keys = new HashMap<WatchKey, Path>();

		walkAndRegisterDirectories(dir);
	}

	/**
	 * Register the given directory with the WatchService; This function will be
	 * called by FileVisitor
	 */
	private void registerDirectory(Path dir) throws IOException {
		WatchKey key = dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
				StandardWatchEventKinds.ENTRY_MODIFY);
		keys.put(key, dir);
	}

	/**
	 * Register the given directory, and all its sub-directories, with the
	 * WatchService.
	 */
	private void walkAndRegisterDirectories(final Path start) throws IOException {
		// register directory and sub-directories
		Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
			@Override
			public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
				registerDirectory(dir);
				return FileVisitResult.CONTINUE;
			}
		});
	}

	/**
	 * Process all events for keys queued to the watcher
	 */
	private void processEvents() {
		for (;;) {

			// wait for key to be signalled
			WatchKey key;
			try {
				key = watcher.take();
			} catch (InterruptedException x) {
				return;
			}

			Path dir = keys.get(key);
			if (dir == null) {
				System.err.println("WatchKey not recognized!!");
				continue;
			}

			for (WatchEvent<?> event : key.pollEvents()) {
				@SuppressWarnings("rawtypes")
				WatchEvent.Kind kind = event.kind();

				// Context for directory entry event is the file name of entry
				@SuppressWarnings("unchecked")
				Path name = ((WatchEvent<Path>) event).context();
				Path child = dir.resolve(name);

				// print out event
				System.out.format("%s: %s\n", event.kind().name(), child);

				// if directory is created, and watching recursively, then register it and its
				// sub-directories
				if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
					try {
						if (Files.isDirectory(child)) {
							walkAndRegisterDirectories(child);
						}
					} catch (IOException x) {
						// do something useful
					}
				}
			}

			// reset key and remove from set if directory no longer accessible
			boolean valid = key.reset();
			if (!valid) {
				keys.remove(key);

				// all directories are inaccessible
				if (keys.isEmpty()) {
					break;
				}
			}
		}
	}
}

Thực thi chương trình trên và thêm/ sửa/ xóa một vài file trong thư mục “C:/uploads”, bạn sẽ thấy kết quả như sau:


ENTRY_CREATE: C:\uploads\image1.png
ENTRY_MODIFY: C:\uploads\image1.png
ENTRY_MODIFY: C:\uploads\image1.png
ENTRY_CREATE: C:\uploads\docs
ENTRY_MODIFY: C:\uploads\docs
ENTRY_CREATE: C:\uploads\images
ENTRY_MODIFY: C:\uploads\images
ENTRY_CREATE: C:\uploads\test.txt
ENTRY_MODIFY: C:\uploads\test.txt
ENTRY_DELETE: C:\uploads\image1.png
ENTRY_DELETE: C:\uploads\test.txt
ENTRY_CREATE: C:\uploads\docs\note.txt
ENTRY_MODIFY: C:\uploads\docs\note.txt
ENTRY_MODIFY: C:\uploads\docs
ENTRY_CREATE: C:\uploads\images\avatar.png
ENTRY_MODIFY: C:\uploads\images\avatar.png
ENTRY_MODIFY: C:\uploads\images
ENTRY_MODIFY: C:\uploads\images\avatar.png

Khi nào cần sử dụng Watch Service API

Watch Service API được thiết kế cho các ứng dụng cần được thông báo về các sự kiện thay đổi file. Nó thích hợp cho bất kỳ ứng dụng nào, như trình soạn thảo hoặc IDE, có khả năng có nhiều file đang mở và cần đảm bảo rằng các file được đồng bộ hóa với hệ thống file. Nó cũng rất thích hợp cho một máy chủ ứng dụng để theo dõi một thư mục, có thể chờ đợi các tệp .jsp hoặc .jar được thêm vào để triển khai, cài đặt chúng.

Tài liệu tham khảo:

  • https://docs.oracle.com/javase/7/docs/api/java/nio/file/WatchService.html
  • https://docs.oracle.com/javase/tutorial/essential/io/notification.html
  • https://howtodoinjava.com/java8/java-8-watchservice-api-tutorial/
4.6
09
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Java I/O Được gắn thẻ: Java 8

Base64 encoding và decoding trong Java 8
Tránh lỗi NullPointerException trong Java như thế nào?

Có thể bạn muốn xem:

  • Hướng dẫn chuyển đổi Java Object sang XML và XML sang Java Object sử dụng Java JAXB (30/12/2017)
  • Giới thiệu luồng vào ra (I/O) trong Java (11/12/2017)
  • Hướng dẫn sử dụng Printing Service trong Java (17/12/2017)
  • Giới thiệu java.io.tmpdir (16/12/2017)
  • Hướng dẫn phân tích nội dung HTML sử dụng thư viện Jsoup (02/01/2018)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (98033 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97681 lượt xem)
  • Giới thiệu Design Patterns (87732 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (86400 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83806 lượt xem)

Nội dung bài viết

  • 1 Giới thiệu
  • 2 Các phương thức được cung cấp bởi Watch Service
  • 3 Các loại sự kiện được đăng ký với Watch Service
  • 4 Các bước để sử dụng Watch Service
  • 5 Ví dụ
  • 6 Khi nào cần sử dụng Watch Service API

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link