Java Academy · Oppitunti

WatchService: tiedostomuutosten valvonta

Rekisteröi WatchService havaitsemaan tiedostojen luonti-, muokkaus- ja poistotapahtumat reaaliajassa.

Oppitunti 4/413 vaihetta

WatchService: tiedostomuutosten valvonta on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mikä on WatchService?

WatchService valvoo hakemistoa tiedostojärjestelmän tapahtumien varalta: luonti, muokkaus ja poisto. Se käyttää käyttöjärjestelmätason ilmoituksia (Linuxissa inotify, macOS:ssä FSEvents) — paljon tehokkaampaa kuin jatkuva kysely.

import java.nio.file.*;

WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Path.of("/home/user/watched");

dir.register(watcher,
    StandardWatchEventKinds.ENTRY_CREATE,
    StandardWatchEventKinds.ENTRY_MODIFY,
    StandardWatchEventKinds.ENTRY_DELETE
);
System.out.println("Watching: " + dir);

Tapahtumien kysely

Kutsukaa watcher.take()-metodia odottamaan estävästi, kunnes tapahtuma ilmenee, ja käsitelkää sitten tapahtumat:

while (true) {
    WatchKey key;
    try {
        key = watcher.take(); // blocks until events arrive
    } catch (InterruptedException e) {
        break;
    }
    
    for (WatchEvent<?> event : key.pollEvents()) {
        WatchEvent.Kind<?> kind = event.kind();
        Path changed = ((WatchEvent<Path>) event).context();
        System.out.println(kind.name() + ": " + changed);
    }
    
    key.reset(); // must reset to receive further events
}

Estämätön kysely

Käyttäkää watcher.poll()-metodia estämättömiin tarkistuksiin tai poll(timeout, unit)-metodia ajastettuun odotukseen:

// Non-blocking — returns null if no events
WatchKey key = watcher.poll();
if (key != null) {
    // process events
    key.reset();
}

// Wait up to 5 seconds:
WatchKey key2 = watcher.poll(5, java.util.concurrent.TimeUnit.SECONDS);
if (key2 != null) {
    // process events
    key2.reset();
}

OVERFLOW-tapahtuma

Jos tapahtumia kertyy nopeammin kuin sovellus käsittelee niitä, OVERFLOW-tapahtuma ilmoittaa joidenkin tapahtumien kadonneen. Käsitelkää tilanne hallitusti:

for (WatchEvent<?> event : key.pollEvents()) {
    if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
        System.out.println("OVERFLOW: some events missed, rescanning directory");
        // rescan dir to find current state
        continue;
    }
    // process normal events
}

Täydellinen toimiva esimerkki

Täydellinen tiedostojen valvontaohjelma, joka suoritetaan taustasäikeessä:

class FileWatcher implements Runnable {
    private final WatchService watcher;
    
    FileWatcher(Path dir) throws IOException {
        watcher = FileSystems.getDefault().newWatchService();
        dir.register(watcher,
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY,
            StandardWatchEventKinds.ENTRY_DELETE);
    }
    
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                WatchKey key = watcher.take();
                for (WatchEvent<?> e : key.pollEvents())
                    System.out.println(e.kind() + ": " + e.context());
                key.reset();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

Useiden hakemistojen valvonta

Rekisteröikää useita hakemistoja samaan WatchServiceen. Tunnistakaa palautetun WatchKey-avaimen avulla, mistä hakemistosta tapahtuma on peräisin:

Map<WatchKey, Path> keyMap = new HashMap<>();

for (Path dir : List.of(Path.of("/logs"), Path.of("/config"))) {
    WatchKey key = dir.register(watcher,
        StandardWatchEventKinds.ENTRY_MODIFY);
    keyMap.put(key, dir);
}

WatchKey key = watcher.take();
Path dir = keyMap.get(key);
System.out.println("Changed in: " + dir);

Täyden polun selvittäminen

Tapahtuman context() palauttaa vain tiedostonimen, ei täydellistä polkua. Selvittäkää polku valvotun hakemiston perusteella:

WatchEvent<Path> pathEvent = (WatchEvent<Path>) event;
Path filename = pathEvent.context();
Path fullPath = watchedDir.resolve(filename);
System.out.println("Full path: " + fullPath);

Hakemistojen rekursiivinen valvonta

WatchService valvoo vain rekisteröityä hakemistoa, ei sen alihakemistoja. Käykää hakemistopuu läpi ja rekisteröikää jokainen alihakemisto rekursiivista valvontaa varten:

WatchService watcher = FileSystems.getDefault().newWatchService();
Path root = Path.of("/project");

try (var stream = Files.walk(root)) {
    stream.filter(Files::isDirectory)
          .forEach(dir -> {
              try { dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,
                  StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
              } catch (IOException e) { throw new RuntimeException(e); }
          });
}

Resurssien vapauttaminen

Sulkekaa WatchService aina, kun olette valmiit, jotta käyttöjärjestelmän resurssit vapautuvat:

try (WatchService watcher = FileSystems.getDefault().newWatchService()) {
    Path dir = Path.of("/tmp/watch");
    dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
    
    WatchKey key = watcher.poll(10, java.util.concurrent.TimeUnit.SECONDS);
    if (key != null) {
        key.pollEvents().forEach(e -> System.out.println(e.context()));
        key.reset();
    }
} // watcher.close() called automatically

Käytännön käyttötapaus: asetusten hot reload

Käyttäkää WatchServiceä asetusten lataamiseen uudelleen, kun tiedosto muuttuu:

class ConfigWatcher {
    private volatile Map<String, String> config = new HashMap<>();
    
    void loadConfig(Path file) throws IOException {
        config = Files.readAllLines(file).stream()
            .filter(l -> l.contains("="))
            .collect(Collectors.toMap(
                l -> l.split("=")[0].trim(),
                l -> l.split("=")[1].trim(),
                (a,b) -> b
            ));
    }
    
    // Call loadConfig again on ENTRY_MODIFY event
}

WatchServicen rajoitukset

Tärkeät rajoitukset:

  • Valvoo vain hakemistoja, ei yksittäisiä tiedostoja
  • Ei ole oletusarvoisesti rekursiivinen — rekisteröikää jokainen alihakemisto manuaalisesti
  • macOS:ssä käyttää sisäisesti kyselyä (reagoi hitaammin kuin Linuxin inotify)
  • Saattaa ohittaa nopeasti peräkkäisten muutosten sarjoja (OVERFLOW)
  • WatchKey muuttuu kelpaamattomaksi, jos valvottu hakemisto poistetaan

Pikatarkistus

Mitä on kutsuttava WatchKey-avaimen tapahtumien käsittelyn jälkeen, ennen kuin WatchService toimittaa kyseiselle avaimelle lisää tapahtumia?

Kertaus: WatchService

Tärkeimmät asiat:

  • Rekisteröikää hakemistot ENTRY_CREATE-, ENTRY_MODIFY- ja ENTRY_DELETE-tapahtumille
  • take() estää säikeen; poll() on estämätön; poll(timeout, unit) soveltuu ajastettuun odotukseen
  • Kutsukaa aina key.reset() tapahtumien käsittelyn jälkeen
  • context() palauttaa vain tiedostonimen — selvittäkää täydellinen polku valvotun hakemiston perusteella
  • Sulkekaa WatchService try-with-resources-rakenteella
  • Rekursiivinen valvonta edellyttää jokaisen alihakemiston rekisteröintiä
Aloita maksutta

Opi Java tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
104
Oppitunnit
374

Usein kysytyt kysymykset

Onko oppitunti ”WatchService: tiedostomuutosten valvonta” ilmainen?

Kyllä – oppitunnin ”WatchService: tiedostomuutosten valvonta” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”WatchService: tiedostomuutosten valvonta”?

Rekisteröi WatchService havaitsemaan tiedostojen luonti-, muokkaus- ja poistotapahtumat reaaliajassa. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”WatchService: tiedostomuutosten valvonta”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?

Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Path: tiedostojen sijaintien esittäminen
  2. Files-apuohjelma: lue, kirjoita, kopioi ja siirrä
  3. Hakemistopuiden läpikäynti Files.walk-metodilla
  4. WatchService: tiedostomuutosten valvonta
← Takaisin: Java Academy