Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Monitorare i cambiamenti dei file in una directory con Java

Un esempio pratico basato su Java per la gestione della funzionalità file change notification con cui monitorare i cambiamenti a carico dei file contenuti in una directory..
Un esempio pratico basato su Java per la gestione della funzionalità file change notification con cui monitorare i cambiamenti a carico dei file contenuti in una directory..
Link copiato negli appunti

Utilizzando un IDE (come per esempio Eclipse o Visual Studio), oppure un qualsiasi word processor, vi sarà capitato di vedere una finestra di dialogo che informa riguardo alle modifiche avvenute a carico di un file sul quale si sta lavorando. Questa funzionalità è detta file change notification e permette ad un programma di monitorare le modifiche avvenute su una particolare directory (watching).

Il watching potrebbe essere implementato facilmente come una successione di richieste inoltrate ad intervalli regolari al file system (polling), ma questa soluzione risulta essere piuttosto inefficiente, in quanto impegna molto la CPU, specialmente nel caso in cui sia necessario monitorare una directory che contiene numerosi file e/o sottodirectory.

In questo articolo parleremo di come Java implementa il watching come una API presente all'interno del package java.nio.file. L'API in questione e basata sul concetto di Watch Service, l'implementazione nativa del file change notification presente in alcuni filesystem di Linux e all'interno di Windows. Nel caso in cui il filesystem non abbia un implementazione nativa, l'API sfrutterà il polling per poter ricavare gli eventi.

Funzionamento base

I passi principali da compiere per usare il watching in Java sono i seguenti:

  • Creazione di un apposito WatchService per il proprio filesystem usando l'interfaccia WatchService java.nio.file
  • Registrazione di ogni directory che si vuol monitorare presso il WachService creato, specificando il tipo di evento che si desidera ottenere, facendo ciò il WatchService restituirà una WatchKey
  • Implementazione di un ciclo infinito per poter gestire gli eventi in arrivo. Quando un evento si verifica, la WatchKey a cui è associato viene posizionata all'interno di una coda presente nel WatchService.
  • Recupero della WatchKey dalla coda del WatcherService.
  • Recupero e gestione di ogni evento pendente associato a quella determitata key (a cui possono essere associati più eventi).
  • Reset della WatchKey e gestione degli altri eventi in attesa.
  • Chiusura del servizio che può avvenire o tramite una chiamata esplicita ad un metodo di chiusura oppure quando termina il thread in cui si era creato il WatchService.
  • Per poter spiegare meglio il funzionamento di questa API, verrà usato come esempio una semplice applicazione, denominata "Email", che simula il watching di una cartella contenente file che rappresentano mail in scrittura. In particolare, per ogni nuovo file di testo salvato nella cartella verrà visualizzato il nome del file appena creato se esso è effettivamente un file di testo.

    Creazione di un Watch Service e registrazione agli eventi

    Il primo passo che l'applicazione Email deve svolgere e quello di creare un nuovo WatchService usando il metodo newWatchService della classe

    FileSystems nel seguente modo:

    public Email(Path dir) throws IOException {
            this.watcher = FileSystems.getDefault().newWatchService();
    	  …
    }

    Successivamente, si possono registrare uno o più oggetti che implementano l'interfaccia Watchable Path register(WatchService, WatchEvent.Kind...) WatchEvent.Kind

    Le istanze più comuni sono contenute nel package java.nio.file.StandardWatchEventKinds e definiscono chiaramente gli eventi:

    Istanza Evento
    ENTRY_CREATE creazione di un nuovo elemento nella directory.
    ENTRY_DELETE cancellazione di un elemento nella directory.
    ENTRY_MODIFY modifica di un elemento.
    OVERFLOW indica che l'evento è stato perso oppure "assorbito" da altre modifiche avvenute successivamente. Non è possibile effettuare una registrazione per quanto riguarda quest'evento.

    Il codice che segue, anch'esso presente nel costruttore della classe Email, mostra come effettuare la registrazione di un oggetto Path presso il WatchService, tale registrazione si riferisce al solo evento di creazione di nuovi file.

    …
    dir.register(watcher, ENTRY_CREATE);
    ..

    Gestione degli eventi

    La gestione di un singolo evento proveniente dal WatchService si svolge in quattro fasi che sono:

    1. Ottenimento di una WatchKey con uno dei seguenti tre metodi che differiscono tra loro per essere più o meno bloccanti:
      1. poll
      2. poll(long, TimeUnit) timeout TimeUnit
      3. take
    2. Elaborazione delle richieste pendenti presenti per ogni WatchKey ottenuta nel passaggio precedente; in particolare verrà elaborata la lista degli WatchEvents ottenuta tramite il metodo pollEvents
    3. Recupero del tipo di evento utilizzando il metodo kind OVERFLOW
    4. Recupero del nome del file associato all'evento, tale informazione è memorizzata nel contest dell'evento, per poterlo ricavare basta richiamare il metodo context
    5. Una volta che tutti gli eventi di una WatchKey sono stati elaborati, è necessario far si che il WatchKey sia di nuovo pronto per poter ricevere nuovi eventi. Per far ciò si utilizza il metodo reset

    Come anticipato nell'ultimo punto, una WatchKey ha uno stato e, in particolare, può assumere uno dei seguenti stati:

    Stato Descrizione
    Ready il WatchKey è pronto a ricevere nuovi eventi. Tale stato viene assunto al momento della creazione della WatchKey.
    Signaled indica che uno o più eventi sono presenti in coda. Una volta che la WatchKey è in questo stato, non è più pronta a ricevere nuovi eventi, per poter tornare allo stato Ready bisogna invocare il metodo reset
    Invalid presuppone che la WatchKey non sia più attiva. Ciò può avvenire perchè:

    • Il processo ha esplicitamente cancellato la WatchKey chiamando il metodo cancel
    • La directory associata alla WatchKey diventa inaccessibile.
    • Il WatchService viene chiuso usando il metodo closed

    Nell'esempio mostrato di seguito un metodo denominato processEvents() CREATE probeContentType(Path)

    public void processEvents()
    {
    	while(true)
    	{
    		//Si attende che arrivi un nuovo evento CREATE dalla directory registrata
    		WatchKey key;
    		try {
    			key = watcher.take();
    		} catch (InterruptedException x) {
    			return;
    		}
    		for (WatchEvent<?> event: key.pollEvents())
    		{
    			WatchEvent.Kind kind = event.kind();
    			if (kind == OVERFLOW) continue; 
    			//Recupero del nome del file dal contesto.
    			WatchEvent<Path> ev = (WatchEvent<Path>)event;
    			Path filename = ev.context();
    			//Verifica se il file appena creato è un file di testo.
    			try {
    				Path child = dir.resolve(filename);
    				if (!Files.probeContentType(child).equals("text/plain"))
    				{
    					System.err.format("Il file '%s'non è un file di testo.%n", filename);
    					continue;
    				}
    			} catch (IOException x) {
    				System.err.println(x);
    				continue;
    			}
    			//Stampa del file
    			System.out.format("File %s%n", filename);
    		}
    		//Applichiamo reset a key in modo da poter ricevere un nuovo evento
    		boolean valid = key.reset();
    		if (!valid) break;
    	}
    }

Ti consigliamo anche