また一週の末ですね~
ジャワ教室の先生は「毎週の宿題はブログ一篇」と言いました、だから今週はまた、ボクはここにいる
ボクは今、そんな難しいプログラムをできないんですから、ちょっと面白いとか、ファンクションがあるプログラムをブログに献上します
今週のテーマはディレクトリーの監視器(Monitor)です
指定したディレクトリーに、どのファイルが削除されたとか、新なファイルを作成とか、あるいは別のファイルをこのディレクトリーにコピーしたとか、これらのファイルの動きをコンソールに表示する。これは、この監視器の機能です
先ずは、考えの筋を説明しよう
このプログラムが、スレッドを使用した。スレッドの「sleep()」方法を使い、一定の時間が経たあと、ディレクトリーの中のファイルを旧ファイルをコンペアマッチしたあと、ファイルの変動がわかる
監視器のクラスに、二つのリスト(List)を作った、名は「list」と「newlist」、毎回、マッチングしたあと、新たのファイルリストを「list」に保存して、スリープのあと、あの時のファイルを「newlist」に保存して、そしてマッチングする。マッチングしたあと、ファイルの変動をコンソールに表示する
コードは、多段で書きます
先ずは、準備のコードはここです:
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class Monitor extends Thread {
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private long sleepTime;
private File sourceDirectory;
ArrayList list;// 用于存放旧文件列表
ArrayList newlist;// 用于存放新文件列表
public Monitor(File dir, long sleepTime) {
this.sleepTime = sleepTime;
sourceDirectory = dir;
try {
list = trans(sourceDirectory.listFiles());
} catch (Exception e) {
System.out.println("Can't start Monitor!");
}
}
// 把文件数组转换成文件list
public ArrayList trans(File[] list) {
ArrayList generateList = new ArrayList();
try {
for (int i = 0; i < list.length; i++) {
generateList.add(list[i]);
}
} catch (Exception e) {
}
return generateList;
}
// 获得文件名以及日期
public void getInfor(File f) {
Date d = new Date(System.currentTimeMillis());
System.out.println(f.getAbsolutePath() + "\n\tTime:" + sdf.format(d));
}
上のコードには、このクラス必要の変数とリスト、そしてコンストラクターを書きました、「sleepTime」にスレッドの睡眠時間を保存する
「sourceDirectory」に監視するディレクトリーを保存する。そして「list」と「newlist」
プログラムは、ディレクトリーをもらった時、ファイルリストは必要だ、でも、「listFiles()」方法は、リターンしてくれたのはファイルのアレイ(array)です、このクラスに常に使用しているのはリスト(list)だ。そして、「public ArrayList
ファイルが変動したとき、その変動の時点もアウトプットできるために、この「public void getInfor(File f)」という方法を作った
以上は事前準備です、これからのは本物だ
// 查找文件,返回下表+1
public int search(File target, ArrayList list) {
if (target == null)
return 0;
for (int i = 1; i <= list.size(); i++) {
if (list.get(i - 1).equals(target)) {
return i;
}
}
return 0;
}
// 进行新旧文件列表匹配
public synchronized void matchDirectory(ArrayList f1,
ArrayList f2) {
// 输出当前系统时间,表示准备进行一次匹配(不输出也没关系)
// Date d = new Date(System.currentTimeMillis());
// System.out.println(sdf.format(d));
int index = 0;
for (int i = 0; i < f1.size(); i++) {
index = search(f1.get(i), f2);
if (index == 0) {
System.out.print("Deleted: ");
getInfor(f1.get(i));
}
}
for (int i = 0; i < f2.size(); i++) {
index = search(f2.get(i), f1);
if (index == 0) {
System.out.print("New File: ");
getInfor(f2.get(i));
if (f2.get(i).isDirectory())
// 当创建了子文件夹时,为子文件夹添加监视器
new Monitor(f2.get(i), sleepTime).start();
}
}
}
ここで説明します
もし旧リストは「a.txt, b.txt, c.txt」です
新リストは「a.txt, c.txt」です。旧リストのファイルを一つ一つ新リストに検索、もし新リストにいないなら、このファイルが削除されたとこが分かる
旧リストのa.txtで新リストに検索、結果は1(アレイインデックス+1)、ここに、もし新リストにないなら、0をリターンです、だからa.txtの検索の結果は1
旧リストのb.txtで新リストに検索、結果は0、だから、b.txtが削除された、c.txtは同じです
逆に、もし旧リストは「1.txt, 3.txt, 4.txt」、新リストには「1.txt, 2.txt, 3.txt, 4.txt」新リストのファイルを一つ一つ旧リストに検索
新リストにの「2.txt」は旧リストにない、だから、「2.txt」は新たなファイルです。だから、「matchDirectory」方法には、旧・新、二回の検索がある
こうやって、ディレクトリーの変動が分かる
ちょっと注意するのは、もし新たのディレクトリーを探知したら、そのディレクトリーに同じ監視器を仕掛ける、これはなぜですか、後に説明します
最後は、スレッドの「Run()」方法です
@Override
public void run() {
if (!sourceDirectory.isDirectory()) {
} else {
while (true) {
// 检测被监视文件夹是否被删除,如果被删除,自动关闭该文件夹的监视器
if (!sourceDirectory.exists()) {
break;
}
newlist = trans(sourceDirectory.listFiles());
matchDirectory(list, newlist);
list = (ArrayList) newlist.clone();
try {
sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
「Run()」に、無限ループで、この監視器を動かし続ける、つまり、人がこのプログラムを停止しないなら、かつ、異常がないなら、旧、新リストのマッチングはずっとし続く。このループに、毎回、新リストのデータを旧リストに移動する、そして、この旧リストは、次のループの新リストとマッチングする。そう繰り返して、監視の効果ができる。
ここにも、注意点がある。もし、この監視されているディレクトリーが削除されたら、「break」で、この無限ループを脱出する、こうやって、システム資源を無駄に使用しない
本来、このプログラムはここまででした が
昨日、少し思い溢れた。
この監視器は、一つのディレクトリーしか、監視できません、そして、中のサブディレクトリーも監視できません。だから、「起動すれば、このディレクトリーの全てのサブディレクトリーも監視できるかな」と思いました
そして、「再帰(recurse)」の思想で、この完全監視をできた!
コードはここです
package assignment;
import java.io.File;
public class TotalMonitor extends Thread {
private long sleepTime;
public TotalMonitor(long sleepTime) {
this.sleepTime = sleepTime;
}
// 递归启动监视器
public void startMonitor(File sourceDirectory) {
new Monitor(sourceDirectory, sleepTime).start();
File[] list = sourceDirectory.listFiles();
for (File f : list) {
if (f.isDirectory()) {
startMonitor(f);
}
}
}
@Override
public void run() {
while (true) {
// 显示当前监控文件夹数量
System.out.println("Active Threads: " + activeCount());
System.gc();
try {
sleep(sleepTime);
} catch (InterruptedException e) {
}
}
}
public static void main(String[] args) {
File f = new File("e:");
TotalMonitor TM = new TotalMonitor(5000);
TM.startMonitor(f);
TM.start();
}
}
ちなみに、この二つのクラスは同じパッケージです
皆さん、ディレクトリーのトラバーサル(directory traversal)が知っているでしょう、ディレクトリーにディレクトリーを探知したら、そのサブディレクトリーをトラバーサルする
これもおなじ、このクラスの「startMonitor()」方法は、ディレクトリーのトラバーサルと同様で、違うのは、もしディレクトリーを探知したら、上の監視器(Monitor)を仕掛ける、つまり、このクラスは本当の監視器ではない、ただ起動するとき、全てのディレクトリーを監視器仕掛けするだけです。監視器のクラスはスレッドを引き継ぐから、毎の監視器は別々で、互いに邪魔をしません
では、現在、前に言ったの 「監視器は新たのディレクトリーを探知したら、監視器はを仕掛ける」の原因はこれです、つまり、元の監視器は、その二行のコードは要らないです、でも、全てのディレクトリーを監視するために、すなわち、監視器を起動させたあと、新たのディレクトリーも監視できるために、その二行のコードは必要です。それは、ここの目的とつながっている。
監視器の停止も同じだ、もし監視器は目標(directory)を探知できないなら、その目標の監視器は自動停止機能を備えている
このプログラムはただ授業の宿題の一つで、ボクは前にもっと機能を広げて、このプログラムをできた が
このプログラムには、多数の不具合があると知っている、たとえ、ファイルの再命名が探知できない、このプログラムでの表現は削除された、そして名が違うファイルができた、実は同じファイルです。恥ずかしいけど、ボクは今、この不具合を修正できません。
だから今、ボクは頑張って、もっと勉強して、いつかこの不具合を修正できるようになっていく
では、今週のブログはここです、ご覧くださってありがとうございます~