特定ディレクトリのファイル更新をチェックするJavaTips 〜Javaプログラミング編

» 2005年04月20日 10時00分 公開
[佐藤匡剛@IT]

 Tomcatには、Webアプリケーション自動配備の仕組みがあります。アプリケーションのベースディレクトリ(デフォルトでは「%TOMCAT_HOME%\webapps」)以下にアプリケーションを追加したり、アプリケーションを変更したときに、自動的にロードしてくれる仕組みです。

 こうした、特定ディレクトリ以下のファイル更新を自動的にチェックするような機能は、Javaの基本的なテクニックの組み合わせで実現できます。本TIPSで説明する方法は、Tomcatで実際に使われています。

更新自動チェックのプログラミング方法

 スレッドを1つ常駐させて一定間隔でチェック処理を走らせます。ここで、ディレクトリ更新の自動チェックを行うクラスを作成する上でのポイントを、5点挙げます。

  • (1)Runnableインターフェイスを実装した内部クラスを作る
  • (2)スレッドを停止するためのフラグを持つ
  • (3)管理下に置かれているファイルのリストを持つ
  • (4)管理下に置かれているファイルの最終更新時刻を記録する
  • (5)常駐させるスレッドはデーモンスレッドとする

 (1)と(5)について簡単に補足します。まず、(1)でなぜ内部クラスにするかというと、このRunnable実装クラスは、本体クラスと強い関連を持つクラスなので、外で定義したところでほかに再利用されることが極めて考えにくいためです。外に見えるクラス数を不必要に多くし、プログラマを混乱させないためにも、このクラスは内部クラスとした方が良いでしょう。

 (5)でスレッドをデーモンスレッド()にするのは、プログラム本来の動作が終了したときにプログラムが終了するようにするためです。例えばTomcatの場合、本来の動作とはサーブレット・コンテナとしての動作です。従って、ディレクトリ更新の自動チェックだけが目的のプログラムであれば、逆にデーモンスレッドにしてはいけません。

注:Javaでは通常、スレッドが生きている限りプログラムは終了しませんが、生きているスレッドがデーモンスレッドだけになった場合、プログラムは終了することになっています。

 更新チェッククラスは、以下のようになります。

AutoDirCheck.java
 package net.mogra.wings.javatips;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class AutoDirCheck {

  // 更新の監視対象となるディレクトリ
  private static final File TARGET_DIR = new File("my_dir");

  protected boolean fStop;          //(2)スレッドの停止フラグ
  protected List    fRegistereds;   //(3)登録されているファイルのリスト
  protected Map     fLastModifieds; //(4)登録されているファイルの最終更新時刻

  /**
   * 更新チェックの開始
   */

  public void start() {
    fStop = false;
    fRegistereds = new ArrayList();
    fLastModifieds = new HashMap();
    Thread thread = new Thread(new AutoChecker());
    thread.setDaemon(true); //(5)デーモンスレッドにする
    thread.start();
  }

  /**
   * 更新チェックの終了
   */

  public void stop() {
    fStop = true;
  }

  /**
   * 1回のチェック処理
   *
/
  protected void check() {
    checkRemoved();  // 削除チェック
    checkNew();      // 新規チェック
    checkModified(); // 更新チェック
  }

  /**
   * 削除されたファイルのチェック
   */

  protected void checkRemoved() {
    Iterator it = fRegistereds.iterator();
    while (it.hasNext()) {
      String filename = (String) it.next();
      File file = new File(TARGET_DIR, filename);
      if (!file.exists()) {
        // 削除処理
        it.remove();
        System.out.println(filename + " が削除されました");
      }
    }
  }

  /**
   * 新たに追加されたファイルのチェック
   */

  protected void checkNew() {
    String[] files = TARGET_DIR.list();
    for (int i = 0; i < files.length; i++) {
      if (!fRegistereds.contains(files[i])) {
        // 追加処理
        fRegistereds.a
dd(files[i]);
        System.out.println(files[i] + " が追加されました");
      }
    }
  }

  /**
   * 更新されたファイルのチェック
   */

  protected void checkModified() {
    Iterator it = fRegistereds.iterator();
    while (it.hasNext()) {
      String filename = (String) it.next();
      File file = new File(TARGET_DIR, filename);
      Long lastModified = (Long) fLastModifieds.get(filename);
      long newLastModified = file.lastModified();
      if (lastModified == null) {
        fLastModifieds.put(filename, new Long(newLastModified));
      } else {
        // 更新処理
        if (lastModified.longValue() < newLastModified) {
          fLastModifieds.put(filename, new Long(newLastModified));
          System.out.println(filename + " が更新されました");
        }
      }
    }
  }

  /**
   * (1)バックグラウンドで走る更新チェックスレッド
   */
  protected class AutoChecker implements Runnable {

    public void run() {
      while (!fStop) {
        try {
          Thread.sleep(1000L); // チェック間隔
        } catch (InterruptedException e) {}
        check();
      }
    }
  }
}


 サンプルクラスでは、ファイルの追加/更新/削除があったときに、それを標準出力へ知らせるだけの処理をしています。この処理を変更したければ、サンプルクラスのcheckRemoved()、checkNew()、checkModified()の各メソッドを修正すればよいでしょう。これらのメソッドはprotected属性になっていますので、このクラスをサブクラス化して、各メソッドの実装をオーバーライドすることで、動作の変更ができます。

 さて、このプログラムを実行させるためのクライアントコードは以下のようになります。

AutoDirCheckClient.java
package net.mogra.wings.javatips;
public class AutoDirCheckClient {
  public static void main(String[] args) {
    AutoDirCheck check = new AutoDirCheck();
    check.start();
    try {
      // 1分間プログラムを走らせる
      Thread.sleep(60 * 1000L);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    check.stop();
  }
}


 プログラムを実行させてみます。実行の前に、カレントディレクトリに「my_dir」ディレクトリがあることを確認してください。実行後にファイルの追加/更新/削除を行うと、以下のように表示されます。

 > java net.mogra.wings.javatips.AutoDirCheckClient
1.txt が追加されました
2.txt が追加されました
3.txt が追加されました
2.txt が更新されました
1.txt が更新されました
2.txt が削除されました
3.txt が削除されました
1.txt が削除されました


Profile

WINGSプロジェクト

佐藤匡剛


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。