Um guia para WatchService no Java NIO2
1. Visão geral
Neste artigo, vamos explorar a interfaceWatchService das APIs do sistema de arquivos Java NIO.2. Este é um dos recursos menos conhecidos das APIs IO mais recentes que foram introduzidas no Java 7 junto com a interfaceFileVisitor.
Para usar a interfaceWatchService em seus aplicativos, você precisa importar as classes apropriadas:
import java.nio.file.*;
2. Por que usarWatchService
Um exemplo comum para entender o que o serviço faz é realmente o IDE.
Você deve ter notado que os IDEs sempredetects a change in source code files que acontecem fora dele. Alguns IDE informam você usando uma caixa de diálogo para que você possa escolher recarregar o arquivo do sistema de arquivos ou não, outros simplesmente atualizam o arquivo em segundo plano.
Da mesma forma, estruturas mais novas, como Play, também recarregam o código do aplicativo a quente por padrão - sempre que você estiver realizando edições em qualquer editor.
Esses aplicativos empregam um recurso chamadofile change notification que está disponível em todos os sistemas de arquivos.
Basicamente,we can write code to poll the filesystem for changes on specific files and directories. No entanto, esta solução não é escalável, especialmente se os arquivos e diretórios atingirem centenas e milhares.
No Java 7 NIO.2, a APIWatchService fornece uma solução escalonável para monitorar diretórios para mudanças. Ele tem uma API limpa e é tão bem otimizado para desempenho que não precisamos implementar nossa própria solução.
3. Como funciona o Watchservice?
Para usar os recursosWatchService, a primeira etapa é criar uma instânciaWatchService usando a classejava.nio.file.FileSystems:
WatchService watchService = FileSystems.getDefault().newWatchService();
Em seguida, precisamos criar o caminho para o diretório que queremos monitorar:
Path path = Paths.get("pathToDir");
Após esta etapa, precisamos registrar o caminho no serviço de observação. Existem dois conceitos importantes para entender nesta fase. A classeStandardWatchEventKinds e a classeWatchKey. Dê uma olhada no seguinte código de registro apenas para entender onde cada queda. Seguiremos isso com explicações do mesmo:
WatchKey watchKey = path.register(
watchService, StandardWatchEventKinds...);
Observe apenas duas coisas importantes aqui: primeiro, a chamada da API de registro de caminho leva a instância do serviço de observação como o primeiro parâmetro seguido por argumentos variáveis deStandardWatchEventKinds. Em segundo lugar, o tipo de retorno do processo de registro é uma instânciaWatchKey.
3.1. The StandardWatchEventKinds
Esta é uma classe cujas instâncias informam ao serviço de observação os tipos de eventos a serem observados no diretório registrado. Atualmente, existem quatro eventos possíveis a serem observados:
-
StandardWatchEventKinds.ENTRY_CREATE - disparado quando uma nova entrada é feita no diretório monitorado. Pode ser devido à criação de um novo arquivo ou à renomeação de um arquivo existente.
-
StandardWatchEventKinds.ENTRY_MODIFY - disparado quando uma entrada existente no diretório monitorado é modificada. Todas as edições de arquivo acionam este evento. Em algumas plataformas, mesmo a alteração dos atributos do arquivo o acionará.
-
StandardWatchEventKinds.ENTRY_DELETE - disparado quando uma entrada é excluída, movida ou renomeada no diretório monitorado.
-
StandardWatchEventKinds.OVERFLOW - disparado para indicar eventos perdidos ou descartados. Não vamos nos concentrar muito nisso
3.2. The WatchKey
Esta classe representa o registro de um diretório no serviço de monitoramento. Sua instância é retornada a nós pelo serviço de monitoramento quando registramos um diretório e quando perguntamos ao serviço de monitoramento se ocorreu algum evento registrado.
O serviço Watch não oferece métodos de retorno de chamada que são chamados sempre que um evento ocorre. Só podemos pesquisá-lo de várias maneiras para encontrar essas informações.
Podemos usar a APIpoll:
WatchKey watchKey = watchService.poll();
Essa chamada à API retorna imediatamente. Retorna a próxima chave de monitoramento na fila, cujos eventos ocorreram ou nulos, se nenhum evento registrado ocorreu.
Também podemos usar uma versão sobrecarregada que leva um argumentotimeout:
WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);
Essa chamada de API é semelhante à anterior em valor de retorno. No entanto, ele bloqueia portimeout unidades de tempo para fornecer mais tempo dentro do qual um evento pode ocorrer em vez de retornar null imediatamente.
Finalmente, podemos usar a APItake:
WatchKey watchKey = watchService.take();
Essa última abordagem simplesmente bloqueia até que um evento ocorra.
Devemos observar algo muito importante aqui:when the WatchKey instance is returned by either of the poll or take APIs, it will not capture more events if it’s reset API is not invoked:
watchKey.reset();
Isso significa que a instância da chave de monitoramento é removida da fila do serviço de monitoramento toda vez que é retornada por uma operação de pesquisa. A chamada de APIreset o coloca de volta na fila para aguardar mais eventos.
A aplicação mais prática do serviço inspetor exigiria um loop no qual verificamos continuamente as alterações no diretório monitorado e processamos adequadamente. Podemos usar o seguinte idioma para implementar isso:
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent> event : key.pollEvents()) {
//process
}
key.reset();
}
Criamos uma tecla de observação para armazenar o valor de retorno da operação de pesquisa. O loop while será bloqueado até que a instrução condicional retorne com uma tecla de observação ou nula.
Quando obtemos uma tecla de relógio, o loop while executa o código dentro dela. Usamos a APIWatchKey.pollEvents para retornar uma lista de eventos que ocorreram. Em seguida, usamos um loopfor each para processá-los um por um.
Depois que todos os eventos são processados, devemos chamar a APIreset para enfileirar a chave de observação novamente.
4. Exemplo de monitoramento de diretório
Visto que cobrimos a APIWatchService na subseção anterior e como ela funciona internamente e também como podemos usá-la, podemos agora prosseguir e ver um exemplo completo e prático.
Por motivos de portabilidade, observaremos a atividade no diretório inicial do usuário, que deve estar disponível em todos os sistemas operacionais modernos.
O código contém apenas algumas linhas de código, portanto, apenas o manteremos no método principal:
public class DirectoryWatcherExample {
public static void main(String[] args) {
WatchService watchService
= FileSystems.getDefault().newWatchService();
Path path = Paths.get(System.getProperty("user.home"));
path.register(
watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
WatchKey key;
while ((key = watchService.take()) != null) {
for (WatchEvent> event : key.pollEvents()) {
System.out.println(
"Event kind:" + event.kind()
+ ". File affected: " + event.context() + ".");
}
key.reset();
}
}
}
E isso é tudo o que realmente precisamos fazer. Agora você pode executar a classe para começar a assistir a um diretório.
Quando você navega para o diretório inicial do usuário e realiza qualquer atividade de manipulação de arquivos, como criar um arquivo ou diretório, alterar o conteúdo de um arquivo ou até mesmo excluir um arquivo, tudo será registrado no console.
Por exemplo, supondo que você vá para a casa do usuário, clique com o botão direito no espaço, selecione `new – > file` para criar um novo arquivo e nomeie-o comotestFile. Então você adiciona algum conteúdo e salva. A saída no console terá a seguinte aparência:
Event kind:ENTRY_CREATE. File affected: New Text Document.txt.
Event kind:ENTRY_DELETE. File affected: New Text Document.txt.
Event kind:ENTRY_CREATE. File affected: testFile.txt.
Event kind:ENTRY_MODIFY. File affected: testFile.txt.
Event kind:ENTRY_MODIFY. File affected: testFile.txt.
Sinta-se livre para editar o caminho para apontar para qualquer diretório que você deseja assistir.
5. Conclusão
Neste artigo, exploramos alguns dos recursos menos usados disponíveis nas APIs do sistema de arquivos Java 7 NIO.2, particularmente a interfaceWatchService.
Também conseguimos seguir as etapas de criação de um aplicativo de observação de diretório para demonstrar a funcionalidade.
E, como sempre, o código-fonte completo dos exemplos usados neste artigo está disponível emGithub project.