1概要

この記事では、Java NIO.2ファイルシステムAPIの

WatchService

インターフェースについて探ります。これは、Java 7で

FileVisitor

インタフェースと一緒に導入された新しいIO APIのあまり知られていない機能の1つです。

アプリケーションで

WatchService

インターフェースを使用するには、適切なクラスをインポートする必要があります。

import java.nio.file.** ;


2

WatchService


を使用する理由

サービスの機能を理解するための一般的な例は、実際にはIDEです。

IDEは、それ自体の外部で起こるソースコードファイルの変更を常に検出することに気付いたかもしれません。ファイルシステムからファイルを再ロードするかどうかを選択できるようにダイアログボックスを使用して通知するIDEもあれば、バックグラウンドでファイルを更新するだけのIDEもあります。

同様に、Playなどの新しいフレームワークでも、デフォルトでアプリケーションコードのホットリロードが実行されます。どのエディタからでも編集を実行しているときはいつでも。

これらのアプリケーションはすべてのファイルシステムで利用可能な


ファイル変更通知


と呼ばれる機能を採用しています。

基本的には、** 特定のファイルやディレクトリの変更についてファイルシステムをポーリングするコードを書くことができます。ただし、ファイルとディレクトリが数百と数千に達する場合は特に、この解決策はスケーラブルではありません。

Java 7 NIO.2では、

WatchService

APIはディレクトリの変更を監視するためのスケーラブルなソリューションを提供します。それはきれいなAPIを持ち、パフォーマンスのためにとてもよく最適化されているので、私たちは私たち自身のソリューションを実装する必要はありません。


3

WatchService

はどのように機能しますか?


WatchService

機能を使用するには、まず

java.nio.file.FileSystems

クラスを使用して

WatchService

インスタンスを作成します。

WatchService watchService = FileSystems.getDefault().newWatchService();

次に、監視したいディレクトリへのパスを作成する必要があります。

Path path = Paths.get("pathToDir");

このステップの後、私たちは監視サービスにパスを登録しなければなりません。この段階で理解するべき2つの重要な概念があります。

StandardWatchEventKinds

クラスと

WatchKey

クラス。次の登録コードを見て、それぞれがどこにあたるのかを理解してください。同じことを説明しながらこれに従います。

WatchKey watchKey = path.register(
  watchService, StandardWatchEventKinds...);

ここで重要なことは2つだけです。最初に、パス登録API呼び出しは最初のパラメーターとしてwatchサービスインスタンスを取り、その後に

StandardWatchEventKinds

の可変引数が続きます。次に、登録プロセスの戻り型は

WatchKey

インスタンスです。


3.1.



StandardWatchEventKinds


これは、登録されたディレクトリで監視するイベントの種類をインスタンスが監視サービスに指示するクラスです。現在注目しているイベントは4つあります。



  • StandardWatchEventKinds.ENTRY


    CREATE __ – 新しいエントリがあると発生します

監視ディレクトリに作成されます。新しいファイルの作成または既存のファイルの名前変更が原因である可能性があります。



  • StandardWatchEventKinds.ENTRY


    MODIFY __ – 既存のイベントが発生したときにトリガされます。

監視対象ディレクトリのエントリが変更されます。すべてのファイル編集がこのイベントを引き起こします。プラットフォームによっては、ファイル属性を変更してもそれがトリガーされます。



  • StandardWatchEventKinds.ENTRY


    DELETE __ – エントリが

監視ディレクトリ内で削除、移動、または名前変更



  • StandardWatchEventKinds.OVERFLOW


    – 紛失したことを示すためにトリガーされた

破棄されたイベントあまり焦点を当てない


3.2.



WatchKey


このクラスは、watchサービスへのディレクトリの登録を表します。そのインスタンスは、ディレクトリを登録したとき、および登録したイベントが発生したかどうかを監視サービスに問い合わせたときに、監視サービスによって返されます。

監視サービスでは、イベントが発生したときに呼び出されるコールバックメソッドは提供されていません。この情報を見つけるには、いくつかの方法でしか調べることができません。


poll

APIを使用できます。

WatchKey watchKey = watchService.poll();

このAPI呼び出しはすぐに戻ります。イベントが発生した次のキューに入れられた監視キーを返します。登録されたイベントが発生しなかった場合はnullを返します。


timeout

引数を取るオーバーロードされたバージョンを使うこともできます。

WatchKey watchKey = watchService.poll(long timeout, TimeUnit units);

このAPI呼び出しは、戻り値の点で前のものと似ています。ただし、すぐにnullを返すのではなく、タイムアウト時間単位でイベントが発生する可能性のある時間を増やすためにブロックします。

最後に、

take

APIを使用できます。

WatchKey watchKey = watchService.take();

この最後のアプローチは、イベントが発生するまでブロックするだけです。

ここで非常に重要なことに注意する必要があります。


WatchKey

インスタンスが

poll

または

take

APIのいずれかによって返されるときに、リセットAPIが呼び出されないと、それ以上イベントをキャプチャしません。

watchKey.reset();

これは、監視操作によって返されるたびに監視キーインスタンスが監視サービスキューから削除されることを意味します。

reset

API呼び出しはそれをキューに戻して、さらにイベントを待ちます。

ウォッチャーサービスの最も実用的なアプリケーションは、監視されているディレクトリの変更を継続的にチェックし、それに従って処理するループを必要とします。これを実装するために、次の慣用句を使用できます。

WatchKey key;
while ((key = watchService.take()) != null) {
    for (WatchEvent<?> event : key.pollEvents()) {
       //process
    }
    key.reset();
}

ポーリング操作の戻り値を格納するための監視キーを作成します。

whileループは、条件文が監視キーまたはnullのいずれかで戻るまでブロックします。

監視キーを取得すると、whileループがその中のコードを実行します。発生したイベントのリストを返すには、

WatchKey.pollEvents

APIを使用します。それからそれらを一つずつ処理するために

for each

ループを使います。

すべてのイベントが処理されたら、

reset

APIを呼び出してウォッチキーを再度エンキューする必要があります。


4ディレクトリ監視の例

前のサブセクションで

WatchService

APIをカバーし、それが内部でどのように機能するのか、またそれをどのように使用できるのかについて説明したので、ここで先に進み、完全で実用的な例を見ます。

移植性の理由から、私たちはユーザのホームディレクトリの活動を監視するつもりです。それはすべての最近のオペレーティングシステムで利用可能であるべきです。

このコードには数行のコードしか含まれていないので、メインメソッドでそのまま使用します。

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();
        }
    }
}

そしてそれが私たちが本当にしなければならないことのすべてです。これでクラスを実行してディレクトリの監視を開始できます。

ユーザーのホームディレクトリに移動して、ファイルやディレクトリの作成、ファイルの内容の変更、ファイルの削除などのファイル操作アクティビティを実行すると、すべてコンソールに記録されます。

例えば、あなたがユーザの家に行くと仮定して、スペースを右クリックし、新しいファイルを作成するために `

__new – > file`


を選択し、それに

testFile__と名前を付けます。

その後、コンテンツを追加して保存します。コンソールの出力は次のようになります。

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.

見たいディレクトリを指定してパスを編集してください。


5結論

この記事では、Java 7 NIO.2 – ファイルシステムAPIで使用可能な、あまり使用されていない機能、特に

WatchService

インターフェースについて説明しました。

また、ディレクトリ監視アプリケーションを作成して機能を実証するための手順を実行することもできました。

そして、いつものように、この記事で使われている例の完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-io[Githubプロジェクト]にあります。