[JAVA] WatchService usage memo

Note how to use WatchService to monitor folder and file changes in Java.

environment

OS Window 10

Java

java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

Hello World

package sample.watch;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.Watchable;

import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.WatchEvent.*;

public class Main {

    public static void main(String[] args) {
        WatchService watcher;
        try {
            watcher = FileSystems.getDefault().newWatchService();

            Watchable path = Paths.get("./build");
            path.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        while (true) {
            WatchKey watchKey;
            try {
                watchKey = watcher.take();
            } catch (InterruptedException e) {
                System.err.println(e.getMessage());
                return;
            }

            for (WatchEvent<?> event : watchKey.pollEvents()) {
                Kind<?> kind = event.kind();
                Object context = event.context();
                System.out.println("kind=" + kind + ", context=" + context);
            }
            
            if (!watchKey.reset()) {
                System.out.println("WatchKey is disabled");
                return;
            }
        }
    }
}

Execution result

watchservice.gif

Description

--You can use WatchService to monitor changes in the specified Path.

        WatchService watcher;
        try {
            watcher = FileSystems.getDefault().newWatchService();

            ...
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

--First, get an instance of WatchService withFileSystems.getDefault (). newWatchService ()

import static java.nio.file.StandardWatchEventKinds.*;

...

            Watchable path = Paths.get("./build");
            path.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

--Next, you can start monitoring by passing WatchService to theregister ()method of the Watchable you want to monitor. --Path inherits Watchable, so it can be used as a monitoring target. --Specify the event you want to monitor after the second argument --Specify the constants defined in StandardWatchEventKinds --However, ʻOVERFLOW` does not need to be specified (details will be described later).

        while (true) {
            WatchKey watchKey;
            try {
                watchKey = watcher.take();
            } catch (InterruptedException e) {
                System.err.println(e.getMessage());
                return;
            }

            ...
        }

--When you execute the take () method of WatchService, it waits for processing until the event to be monitored occurs. --A WatchKey object is returned when a monitored event occurs --WatchKey is an object that is created when a monitoring target is registered withregister ()of Watchable, and it controls the monitoring status such as referencing event information that occurred for each monitoring target and canceling monitoring. it can

            for (WatchEvent<?> event : watchKey.pollEvents()) {
                Kind<?> kind = event.kind();
                Object context = event.context();
                System.out.println("kind=" + kind + ", context=" + context);
            }

--You can get the information of the event that occurred (WatchEvent) withpollEvents ()of WatchKey. --The following information can be obtained from WatchEvent --kind: Type of event that occurred --context: APath object with a relative path to the created / modified / deleted entry (file or folder) --count: Number of times this type of event has occurred

            if (!watchKey.reset()) {
                System.out.println("WatchKey is disabled");
                return;
            }

--Finally, execute reset () of WatchKey --Why you have to call reset () later --The return value of reset () is boolean, which tells you if WatchKey is still valid. --If false, the WatchKey is disabled and monitoring cannot continue. --One of the following three conditions is applicable for invalidation

  1. Explicitly canceled with WatchKey.cancel ()
  2. The monitored object (directory, etc.) was deleted and became inaccessible, and was implicitly canceled.
  3. WatchService.close () canceled monitoring from WatchService

WatchKey state change

--WatchKey has two states, READY and SIGNALLED. --When generated by Watchable.register (), it is in the READY state. --Changes to SIGNALLED when an event is detected --When you execute WatchKey.reset (), it returns to the READY state again.

watchservice.png

Relationship between WatchService and WatchKey

watchservice.jpg

--WatchService stores the WatchKey created when you executed Watchable.register (). --Also, it holds a queue filled with only WatchKey whose status is SIGNALLED. --The WatchKey immediately after it is created is in the READY state, so it is not in the queue. --When a monitored event is detected, the status of the corresponding WatchKey is changed from READY to SIGNALLED. --The WatchKey whose status is SIGNALLED is added to the queue of WatchService. --Client-side implementations using WatchService can retrieve WatchKey at the beginning of the queue by using the WatchService.take () or poll () methods (removed from the queue). Ru) --take () blocks processing when the queue is empty and waits for WatchKey to be added to the queue -- poll () returns null without blocking if the queue is empty --For poll (), you can specify the timeout time as an argument and let it wait until then. --The WatchKey that was dequeued withtake ()orpoll ()will not be requeued as is. --The state of WatchKey removed from the queue is still SIGNALLED --If an additional monitored event occurs when WatchKey is SIGNALLED, it will not be requeued. --The event information itself that occurred is properly added to WatchKey --WatchKey is made thread-safe, so even if event information is added during event processing, the internal state will not be corrupted. --pollEvents () returns a copy of the event information that WatchKey had at that time. --In addition, the event information acquired by pollEvents () has been deleted from the inside of WatchKey. --In short --Once removed from the queue, WatchKey will not be queued again if left untouched. --If you are not in the queue, you cannot retrieve it with take () or poll (). That means --The state must be returned to READY in order for WatchKey to be queued again. --To return the state of WatchKey to READY, execute the reset () method of WatchKey. --If the event information held by WatchKey is empty (no unprocessed events), the status returns to READY. --After that, when another event occurs, it becomes SIGNALLED and is added to the queue. --If the event information is not empty (there are unprocessed events), it will be added to the queue in the SIGNALLED state.

Summary,

--The WatchKey immediately after creation is in the READY state. --If an event is detected and changed to SIGNALLED, it will be added to the queue in WatchService. --You can take WatchKey from the queue withtake (),poll () --Once out of the queue, WatchKey will not return to the queue without permission unless you executereset (). --If there are unprocessed events, they can be queued as SIGNALLED. If there are no unprocessed events, it will be in the READY state, and the next time the monitored event occurs, it will be SENGALLED again and queued.

OVERFLOW event

package sample.watch;

...

public class Main {

    public static void main(String[] args) {
        ...

        while (true) {
            WatchKey watchKey;
            try {
                watchKey = watcher.take();
                Thread.sleep(1000);★ Addition
            } catch (InterruptedException e) {
                ...
            }

            ...
        }
    }
}

Execution result

watchservice.gif

Description

--A special event called ʻOVERFLOWis used if an event is lost or destroyed for any reason. --For example, depending on the execution environment, there may be an upper limit on the number of events stored inWatchKey, and when an event that exceeds the upper limit occurs, the ʻOVERFLOW event is used. --In the above example, wait for 1 second before getting the event (pollEvents ()), save all the events of a large number of file generation in WatchKey, and forcibly generate ʻOVERFLOW. Is --ʻOVERFLOW event is notified when the condition is met even if it is not specified as an event to be monitored by registration with Watchable.register (). --Therefore, it is better to always consider the processing when ʻOVERFLOW` occurs. --If you can ignore it, ignore it, if you make an error, make an error (it depends on the application you are making)

Monitor multiple folders

package sample.watch;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.Watchable;

import static java.nio.file.StandardWatchEventKinds.*;
import static java.nio.file.WatchEvent.*;

public class Main {

    public static void main(String[] args) {
        WatchService watcher;
        WatchKey fooKey;
        WatchKey barKey;
        try {
            watcher = FileSystems.getDefault().newWatchService();

            Watchable foo = Paths.get("./build/foo");
            fooKey = foo.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

            Watchable bar = Paths.get("./build/bar");
            barKey = bar.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        while (true) {
            WatchKey watchKey;
            try {
                watchKey = watcher.take();
            } catch (InterruptedException e) {
                System.err.println(e.getMessage());
                return;
            }

            for (WatchEvent<?> event : watchKey.pollEvents()) {
                Kind<?> kind = event.kind();
                
                if (kind == OVERFLOW) {
                    continue;
                }

                Object context = event.context();

                String directory;
                if (watchKey == fooKey) {
                    directory = "foo";
                } else if (watchKey == barKey) {
                    directory = "bar";
                } else {
                    directory = "unknown";
                }

                System.out.println("directory=" + directory + ", kind=" + kind + ", context=" + context);
            }
            
            if (!watchKey.reset()) {
                System.out.println("WatchKey is disabled");
                return;
            }
        }
    }
}

Execution result

watchservice.gif

Description

--One WatchService can monitor multiple folders --In that case, it is necessary to identify which folder the WatchKey obtained byWatchService.take ()points to. --If you save the WatchKey that is returned as the return value ofWatchable.register (), you can compare it to identify which folder the event is in.

reference

--Directory change monitoring (Java? Tutorial> Important classes> Basic I / O)

Recommended Posts

WatchService usage memo
JavaParser usage memo
PlantUML usage memo
JUnit5 usage memo
Spring Shell usage memo
Spring Security usage memo CSRF
Spring Security Usage memo Method security
Spring Security usage memo Remember-Me
Dependency Management Plugin Usage memo
Spring Security usage memo CORS
Spring Security usage memo test
Spring Security usage memo Authentication / authorization
JCA (Java Cryptography Architecture) Usage Memo
Spring Security usage memo response header
Spring Security usage memo session management
Spring Security usage memo Basic / mechanism
Integer memo
docker memo
Spring Security Usage Memo Domain Object Security (ACL)
Lombok memo
Dockerfile memo
Iterator memo
corretto memo
Java memo
AWS memo
Dcokerfile memo
Ruby memo
irb usage
Memo Stream