Datei geändert Listener in Java

104

Ich möchte benachrichtigt werden, wenn eine Datei im Dateisystem geändert wurde. Ich habe nur einen Thread gefunden, der die lastModified File-Eigenschaft abfragt, und diese Lösung ist eindeutig nicht optimal.

cheng81
quelle
Vielen Dank für alle Antworten. Da meine Anforderungen gering sind (ich muss nur eine in meiner Webanwendung geänderte Eigenschaftendatei neu laden. Hauptsächlich, weil der Neustart der gesamten Webanwendung etwas langsam ist), bleibe ich beim Umfragemodell. Zumindest weiß ich jetzt, dass es für diese Fälle keine einfache Lösung gibt.
Cheng81
14
Nur eine Notiz. Als wir dieses Problem lösten, stellten wir fest, dass große Dateien in der Regel langsam eingehen, und der Abfragemechanismus entdeckte häufig eine neue oder geänderte Datei, bevor sie vollständig geschrieben wurde. Um dies zu lösen, wurde eine Zwei-Biss-Lösung eingeführt. Der Poller bemerkte, dass sich eine Datei geändert hatte, benachrichtigte das System jedoch nicht über eine neue / geänderte Datei, bis sie für zwei Polls gleich aussah: dh sich stabilisiert hatte. Viele Badfile-Fehler wurden behoben.
Steve Powell
1
Abgesehen davon leidet Tomcat unter diesem Problem, wenn große WAR-Dateien aus Remote-Quellen in den Ordner webapps abgelegt werden, und das schon lange.
John Rix

Antworten:

21

Auf der niedrigen Ebene besteht die einzige Möglichkeit, dieses Dienstprogramm zu modellieren, darin, einen Thread in einem Verzeichnis abzufragen und die Attribute der Datei zu überwachen. Sie können jedoch Muster verwenden, um einen Adapter für ein solches Dienstprogramm zu entwickeln.

Beispielsweise verfügen j2ee-Anwendungsserver wie Tomcat und andere über eine Funktion zum automatischen Laden, bei der die Anwendung neu gestartet wird, sobald sich der Deployment-Deskriptor oder die Servlet-Klasse ändert.

Sie können die Bibliotheken von solchen Servern verwenden, da der größte Teil des Codes von Tomcat wiederverwendbar und OpenSource ist.

Rutesh Makhijani
quelle
59
Dies gilt nicht mehr für Java 7: Es gibt jetzt eine API dafür, die sich in die Benachrichtigungsdienste des Betriebssystems einbinden kann
Arnout Engelen
Diese API ist äußerst unzureichend und bietet keine Benachrichtigung für Ereignisse zum Schließen von Dateien unter Linux. In einem einfachen Fall funktioniert das Kopieren einer Datei in ein anderes Verzeichnis nicht, wenn sie geschlossen wird.
binarytemple_picsolve
117

Ich habe bereits einen Protokolldateimonitor geschrieben und festgestellt, dass die Auswirkung der Abfrage der Attribute einer einzelnen Datei einige Male pro Sekunde auf die Systemleistung tatsächlich sehr gering ist.

Java 7 hat als Teil von NIO.2 die WatchService-API hinzugefügt

Die WatchService-API wurde für Anwendungen entwickelt, die über Dateiänderungsereignisse benachrichtigt werden müssen.

Stephen Denne
quelle
10
Ich sehe die Beispiele als Beobachtungsverzeichnis, aber was ist mit einer einzelnen Datei?
Archimedes Trajano
@ArchimedesTrajano Die API benachrichtigt Sie, wenn sich eine Datei in einem Verzeichnis geändert hat. Das ausgelöste Ereignis enthält den Namen der Datei, die geändert wurde. So können Sie Ereignisse für eine bestimmte Datei oder Dateien verarbeiten und andere ignorieren.
Michael
@ArchimedesTrajano stackoverflow.com/questions/16251273/…
user1742529
39

Ich verwende die VFS-API von Apache Commons. Hier ist ein Beispiel für die Überwachung einer Datei ohne großen Einfluss auf die Leistung:

DefaultFileMonitor

Telcontar
quelle
27

Es gibt eine Bibliothek namens jnotify , die inotify unter Linux umschließt und auch Windows unterstützt. Ich habe es nie benutzt und weiß nicht, wie gut es ist, aber es ist einen Versuch wert, würde ich sagen.

André
quelle
1
Es funktioniert perfekt und ist super einfach zu bedienen. Ich benutze inotify seit vielen Jahren. Es ist schnell, stabil und zuverlässig
am
8

Java commons-io verfügt über einen FileAlterationObserver . Es führt eine Abfrage in Kombination mit einem FileAlterationMonitor durch. Ähnlich wie bei Commons VFS. Der Vorteil ist, dass es viel weniger Abhängigkeiten hat.

Bearbeiten: Weniger Abhängigkeiten sind nicht wahr, sie sind für VFS optional. Es wird jedoch Java-Datei anstelle der VFS-Abstraktionsschicht verwendet.

Christian
quelle
4

"Weitere NIO-Funktionen" verfügt über eine Dateiüberwachungsfunktion, deren Implementierung vom zugrunde liegenden Betriebssystem abhängt. Sollte in JDK7 sein.

Tom Hawtin - Tackline
quelle
Könnten Sie genauer sein oder einen Link zur Dokumentation posten? Ich kann anscheinend nichts dazu finden ...
Luciano
Es scheint das Paket zu sein java.nio.file. Siehe ein Tutorial .
David L.
4

Ich führe dieses Codeausschnitt jedes Mal aus, wenn ich die Eigenschaftendatei lese, und lese die Datei nur dann tatsächlich, wenn sie seit dem letzten Lesen geändert wurde. Hoffe das hilft jemandem.

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

Ein ähnlicher Ansatz wird in Log4J verwendet FileWatchdog.

Mike Kramer
quelle
2

Sie können Dateiänderungen mit einem FileReader abhören. Bitte sehen Sie das Beispiel unten

// 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();
    }
}
Prasanna Sujeewa
quelle
2

Wenn Sie bereit sind, sich von etwas Geld zu trennen, ist JNIWrapper eine nützliche Bibliothek mit einem Winpack. Sie können Dateisystemereignisse für bestimmte Dateien abrufen. Leider nur Windows.

Siehe https://www.teamdev.com/jniwrapper .

Andernfalls ist der Rückgriff auf nativen Code nicht immer schlecht, insbesondere wenn das beste Angebot ein Abfragemechanismus gegenüber einem nativen Ereignis ist.

Ich habe festgestellt, dass Java-Dateisystemvorgänge auf einigen Computern langsam sein und die Leistung der Anwendung leicht beeinträchtigen können, wenn sie nicht richtig gehandhabt werden.

jdh80
quelle
1

Sie können auch die Apache Commons JCI (Java Compiler Interface) in Betracht ziehen. Obwohl sich diese API auf die dynamische Kompilierung von Klassen zu konzentrieren scheint, enthält sie auch Klassen in ihrer API, die Dateiänderungen überwachen.

Beispiel: http://commons.apache.org/jci/usage.html

onejigtwojig
quelle
1

Das Abrufen der zuletzt geänderten Dateieigenschaft ist jedoch eine einfache, aber effektive Lösung. Definieren Sie einfach eine Klasse, die my erweitert, FileChangedWatcherund implementieren Sie die onModified()Methode:

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

Ähnlich wie bei den anderen Antworten habe ich Folgendes mit File, Timer und TimerTask gemacht, damit dies in festgelegten Intervallen als Hintergrund-Thread-Abfrage ausgeführt wird.

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

  }
}
sjsperry
quelle