Archivo de oyente cambiado en Java

104

Me gustaría recibir una notificación cuando se modifique un archivo en el sistema de archivos. No he encontrado nada más que un hilo que sondea la propiedad lastModified File y claramente esta solución no es óptima.

cheng81
fuente
Gracias por todas las respuestas. Dado que mis necesidades son pequeñas (solo necesito volver a cargar un archivo de propiedades cambiado en mi aplicación web ... principalmente porque reiniciar toda la aplicación web es algo lento), me quedaré con el modelo de encuesta. Al menos ahora sé que no existe una solución sencilla para estos casos.
cheng81
14
Solo una nota. Cuando resolvimos este problema antes, descubrimos que los archivos grandes tienden a llegar lentamente, y el mecanismo de sondeo a menudo descubrió un archivo nuevo o modificado antes de que se escribiera por completo. Para resolver esto, se adoptó una solución de "dos bocados". El encuestador notó que un archivo había cambiado, pero no notificó al sistema de un archivo nuevo / cambiado hasta que se veía igual para dos encuestas: es decir, se había estabilizado. Se corrigieron muchos errores de archivos malos.
Steve Powell
1
Además, Tomcat sufre ese problema al colocar archivos WAR grandes en la carpeta webapps desde fuentes remotas y lo ha hecho durante mucho tiempo.
John Rix

Respuestas:

21

En el nivel bajo, la única forma de modelar esta utilidad es tener un sondeo de subprocesos en un directorio y vigilar los atributos del archivo. Pero puede utilizar patrones para desarrollar un adaptador para dicha utilidad.

Por ejemplo, los servidores de aplicaciones j2ee como Tomcat y otros tienen una función de carga automática en la que tan pronto como cambia el descriptor de implementación o cambia la clase de servlet, la aplicación se reinicia.

Puede usar las bibliotecas de dichos servidores, ya que la mayor parte del código de tomcat es reutilizable y de código abierto.

Rutesh Makhijani
fuente
59
Esto ya no es cierto en Java 7: ahora hay una API para esto que puede conectarse a los servicios de notificación del sistema operativo: blogs.oracle.com/thejavatutorials/entry/…
Arnout Engelen
Esa API es muy inadecuada, no proporciona notificaciones para eventos de cierre de archivos en Linux. Entonces, dado un caso simple, cuando se cierra un archivo, copiarlo a otro directorio no funciona.
binarytemple_picsolve
117

Escribí un monitor de archivo de registro antes y descubrí que el impacto en el rendimiento del sistema de sondear los atributos de un solo archivo, unas pocas veces por segundo, es en realidad muy pequeño.

Java 7, como parte de NIO.2, ha agregado la API WatchService

La API WatchService está diseñada para aplicaciones que necesitan ser notificadas sobre eventos de cambio de archivo.

Stephen Denne
fuente
10
Veo los ejemplos como un directorio de observación, pero ¿qué pasa con un archivo individual?
Archimedes Trajano
@ArchimedesTrajano La API le notifica cuando un archivo en un directorio ha cambiado. El evento que se activa incluye el nombre del archivo que ha cambiado. Por lo tanto, puede manejar eventos para un archivo o archivos determinados e ignorar otros.
Michael
@ArchimedesTrajano stackoverflow.com/questions/16251273/…
user1742529
39

Yo uso la API VFS de Apache Commons, aquí hay un ejemplo de cómo monitorear un archivo sin mucho impacto en el rendimiento:

DefaultFileMonitor

Telcontar
fuente
27

Existe una biblioteca llamada jnotify que incluye inotify en Linux y también tiene soporte para Windows. Nunca lo usé y no sé qué tan bueno es, pero diría que vale la pena intentarlo.

André
fuente
1
Funciona perfectamente y es muy sencillo de usar. He estado usando inotify durante muchos años. Es rápido, estable y confiable
2013
8

Java commons-io tiene un FileAlterationObserver . realiza encuestas en combinación con un FileAlterationMonitor. Similar a commons VFS. La ventaja es que tiene muchas menos dependencias.

editar: Menos dependencias no es cierto, son opcionales para VFS. Pero usa Java File en lugar de la capa de abstracción VFS.

cristiano
fuente
4

"Más funciones de NIO" tiene la función de vigilancia de archivos, cuya implementación depende del SO subyacente. Debería estar en JDK7.

Tom Hawtin - tackline
fuente
¿Podría ser más específico o publicar un enlace a la documentación? Parece que no puedo encontrar nada en esto ...
Luciano
Parece ser el paquete java.nio.file. Vea un tutorial .
David L.
4

Ejecuto este fragmento de código cada vez que voy a leer el archivo de propiedades, y solo leo realmente el archivo si se ha modificado desde la última vez que lo leí. Espero que esto ayude a alguien.

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

En Log4J se utiliza un enfoque similar FileWatchdog.

Mike Kramer
fuente
2

Puede escuchar los cambios de archivos utilizando un FileReader. Por favor, vea el ejemplo a continuación

// 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
fuente
2

Si está dispuesto a desprenderse de algo de dinero, JNIWrapper es una biblioteca útil con un Winpack, podrá obtener eventos del sistema de archivos en ciertos archivos. Desafortunadamente solo windows.

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

De lo contrario, recurrir al código nativo no siempre es malo, especialmente cuando lo mejor que se ofrece es un mecanismo de sondeo frente a un evento nativo.

He notado que las operaciones del sistema de archivos de Java pueden ser lentas en algunas computadoras y pueden afectar fácilmente el rendimiento de la aplicación si no se manejan bien.

jdh80
fuente
1

También puede considerar Apache Commons JCI (Interfaz del compilador de Java). Aunque esta API parece estar enfocada en la compilación dinámica de clases, también incluye clases en su API que monitorean los cambios de archivos.

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

onejigtwojig
fuente
1

Sin embargo, sondear la última propiedad del archivo modificado es una solución simple pero efectiva. Simplemente defina una clase que extienda my FileChangedWatchere implemente el onModified()método:

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
fuente
0

De manera similar a las otras respuestas, así es como lo hice usando File, Timer y TimerTask para permitir que esto se ejecute como un sondeo de subprocesos en segundo plano a intervalos establecidos.

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
fuente