ファイルシステムでファイルが変更されたときに通知を受け取りたいのですが。 lastModified Fileプロパティをポーリングするスレッド以外には何も見つかりませんでした。明らかにこのソリューションは最適ではありません。
低レベルでは、このユーティリティをモデル化する唯一の方法は、ディレクトリでスレッドをポーリングし、ファイルの属性を監視することです。ただし、パターンを使用して、このようなユーティリティ用のアダプタを開発できます。
たとえば、Tomcatなどのj2eeアプリケーションサーバーには自動展開機能があり、デプロイメント記述子が変更されるか、サーブレットクラスが変更されるとすぐにアプリケーションが再起動します。
Tomcatのコードのほとんどは再利用可能であり、オープンソースであるため、このようなサーバーのライブラリを使用できます。
以前にログファイルモニターを作成したことがありますが、1秒に数回、1つのファイルの属性をポーリングすることによるシステムパフォーマンスへの影響は、実際には非常に小さいことがわかりました。
Java 7、NIO.2の一部として WatchService API が追加されました
WatchService APIは、ファイル変更イベントについて通知する必要があるアプリケーション向けに設計されています。
Apache CommonsのVFS APIを使用します。パフォーマンスに大きな影響を与えずにファイルを監視する方法の例を次に示します。
Java commons-ioには FileAlterationObserver があります。 FileAlterationMonitorと組み合わせてポーリングを行います。コモンズVFSに似ています。利点は、依存関係がはるかに少ないことです。
編集:より少ない依存関係は真実ではなく、VFSのオプションです。ただし、VFS抽象化レイヤーの代わりにJava Fileを使用します。
プロパティファイルを読みに行くたびに、このコードスニペットを実行します。実際にファイルを読むのは、最後に読んでから変更されている場合のみです。これが誰かを助けることを願っています。
private long timeStamp;
private File file;
private boolean isFileUpdated( File file ) {
this.file = file;
this.timeStamp = file.lastModified();
if( this.timeStamp != timeStamp ) {
this.timeStamp = timeStamp;
//Yes, file is updated
return true;
}
//No, file is not updated
return false;
}
Log4Jでも同様のアプローチが使用されます FileWatchdog
。
「その他のNIO機能」にはファイル監視機能があり、実装は基盤となるOSに依存します。 JDK7にある必要があります。
FileReaderを使用して、ファイルの変更を聞くことができます。次の例をご覧ください
// File content change listener
private String fname;
private Object lck = new Object();
...
public void run()
{
try
{
BufferedReader br = new BufferedReader( new FileReader( fname ) );
String s;
StringBuilder buf = new StringBuilder();
while( true )
{
s = br.readLine();
if( s == null )
{
synchronized( lck )
{
lck.wait( 500 );
}
}
else
{
System.out.println( "s = " + s );
}
}
}
catch( Exception e )
{
e.printStackTrace();
}
}
あなたがいくらかのお金を手放すつもりなら、JNIWrapperはWinpackを備えた便利なライブラリです。特定のファイルでファイルシステムイベントを取得できます。残念ながら窓のみ。
https://www.teamdev.com/jniwrapper を参照してください。
そうでなければ、特に提供されているのがネイティブイベントに対するポーリングメカニズムである場合、ネイティブコードに頼ることは必ずしも悪いことではありません。
Javaファイルシステムの操作は、一部のコンピューターでは遅くなる可能性があり、適切に処理しないとアプリケーションのパフォーマンスに容易に影響を与える可能性があることに気付きました。
ただし、最後に変更されたファイルプロパティのポーリングは、シンプルでありながら効果的なソリューションです。 FileChangedWatcher
を拡張するクラスを定義し、onModified()
メソッドを実装するだけです。
import Java.io.File;
public abstract class FileChangedWatcher
{
private File file;
public FileChangedWatcher(String filePath)
{
file = new File(filePath);
}
public void watch() throws InterruptedException
{
long currentModifiedDate = file.lastModified();
while (true)
{
long newModifiedDate = file.lastModified();
if (newModifiedDate != currentModifiedDate)
{
currentModifiedDate = newModifiedDate;
onModified();
}
Thread.sleep(100);
}
}
public String getFilePath()
{
return file.getAbsolutePath();
}
protected abstract void onModified();
}
JxFileWatcherと呼ばれるファイルおよびフォルダー監視用の商用クロスデスクトップライブラリがあります。ここからダウンロードできます: http://www.teamdev.com/jxfilewatcher/
また、オンラインで実際に見ることができます: http://www.teamdev.com/jxfilewatcher/onlinedemo/
Spring Integrationは、ディレクトリとファイルを監視するための素晴らしいメカニズムを提供します: http://static.springsource.org/spring-integration/reference/htmlsingle/#files 。クロスプラットフォームであることはかなり確かです(Mac、Linux、Windowsで使用しました)。
Apache Commons JCI(Java Compiler Interface)を検討することもできます。このAPIはクラスの動的なコンパイルに焦点を当てているようですが、ファイルの変更を監視するAPIのクラスも含まれています。
他の回答と同様に、File、Timer、およびTimerTaskを使用して、これを設定された間隔でポーリングするバックグラウンドスレッドとして実行する方法を次に示します。
import Java.io.File;
import Java.util.Timer;
import Java.util.TimerTask;
public class FileModifiedWatcher
{
private static File file;
private static int pollingInterval;
private static Timer fileWatcher;
private static long lastReadTimeStamp = 0L;
public static boolean init(String _file, int _pollingInterval)
{
file = new File(_file);
pollingInterval = _pollingInterval; // In seconds
watchFile();
return true;
}
private static void watchFile()
{
if ( null == fileWatcher )
{
System.out.println("START");
fileWatcher = new Timer();
fileWatcher.scheduleAtFixedRate(new TimerTask()
{
@Override
public void run()
{
if ( file.lastModified() > lastReadTimeStamp )
{
System.out.println("File Modified");
}
lastReadTimeStamp = System.currentTimeMillis();
}
}, 0, 1000 * pollingInterval);
}
}
}