14/01/2012
Se añade a la interfaz JDirMonitor métodos que permiten consultar y obtener, a través del path absoluto, archivos que se encuentren dentro del directorio que se está analizando.
26/05/2012

Se añade en clase "cl.getsoftware.jdirmonitor.task.RecursiveTaskMonitor" capacidad de informar estado tanto de los archivos como de los directorios.

-----------------------------------------------------------------------------------------------------------------------


Hace unos días atrás se presentó un problema en un sistema y parte de la solución que se propuso involucraba leer información escrita en tiempo real desde múltiples archivos de bytes con objetos serializados, procesarlos y delegarlos a un sistema anexo, todo esto en Java.
Lo primero que se me ocurrió fue 'googlear' para encontrar una API que hiciera el trabajo de monitorear cambios en los archivos de un directorio y por alguna oscura razón no encontré nada...

Una vez solucionado el asunto me interesó la idea de crear una API lo suficientemente genérica, configurable y fácil de usar que solucionase dicho 'problema'.Y así nació JDirMonitor!

Entonces JDirMonitor es una API, bajo licencia LGPL que permite monitoriar y 'escuchar' los cambios que presentan todos los archivos que estén dentro de un directorio definido, avisando en su defecto cuáles han sido creados, modificados y eliminados.

Además de esto JDirMonitor posee las siguientes características:
-Definición de múltiples listeners para capturar eventos que suceden sobre los archivos.
-Definición de Filtros a aplicar sobre los archivos para, por ejemplo, discriminar cuáles deben procesarse.
-Definición de Comparators de objetos File para, por ejemplo, definir el orden en el que se deben procesar los distintos archivos.

JDirMonitor ofrece 2 tipos de monitoreos:
1) NormalTaskMonitor: analiza solamente los archivos que se encuentran en el primer nivel del directorio especificado.
2) RecursiveTaskMonitor: analiza todos los archivos que se encuentren dentro del directorio a analizar, es recursivo IN ORDEN, ya que accede a los archivos de todos los subdirectorios (en cualquier nivel de profundidad).

El siguiente código de ejemplo permite monitorear un directorio X con NormalTaskMonitor, un listener y un filtro de archivo:

import java.io.File;
import java.util.concurrent.TimeUnit;

import cl.getsoftware.jdirmonitor.DirectoryMonitor;
import cl.getsoftware.jdirmonitor.MonitorBuilder;
import cl.getsoftware.jdirmonitor.exception.DirectoryMonitorException;
import cl.getsoftware.jdirmonitor.filter.RegexNameFileFilter;
import cl.getsoftware.jdirmonitor.listener.FileListener;
import cl.getsoftware.jdirmonitor.task.NormalTaskMonitor;


public class Test {
    
    public static void main(String[] args) throws DirectoryMonitorException {
        String pathDirectory = "ruta/del/directorio/a/monitorear";
        long timeTaskExecute = 2;
        
        //Clase que permite añadir filtros a archivos y directorios
        //mediante expresiones regulares
        RegexNameFileFilter regexName = 
            new RegexNameFileFilter();
        regexName.addFileNameRegex("[a-zA-Z].*?\\.txt$");
        
        MonitorBuilder builder = new MonitorBuilder(pathDirectory,
             timeTaskExecute, new NormalTaskMonitor(),
             TimeUnit.SECONDS);
        builder.addListener(fileListener);
        builder.setFileFilter( regexName );
        DirectoryMonitor monitor = builder.build();
        monitor.start();
        
        //se deja el hilo main vivo en un ciclo infinito
        //para ver lo que imprimen los listeners en consola
        while( true ){
            try {
                Thread.sleep(10000);
            }
            catch (InterruptedException e) {}
        }
    }
    
    
    public static FileListener fileListener = new FileListener() {
        @Override
        public void fileDeleted(File file) {
            System.out.println("Archivo " + file.getAbsolutePath() + " ha sido BORRADO");
        }
        
        @Override
        public void fileCreated(File file) {
            System.out.println("Archivo " + file.getAbsolutePath() + " ha sido CREADO");
        }
        
        @Override
        public void fileChanged(File file) {
            System.out.println("Archivo " + file.getAbsolutePath() + " ha sido MODIFICADO");
        }
    };
}
Así de simple, en 4 líneas tenemos corriendo un DirectoryMonitor que revisará el directorio periódicamente cada N segundos, nos informará a través del listener los cambios que ocurren en los archivos y con la clase cl.getsoftware.jdirmonitor.filter.RegexNameFileFilter se logra filtrar solo los archivos que posean un nombre que cumpla con la expresión regular definida.

Otra ventaja es que la API te permite codificar tus propias clases de monitoreo, las cuales deben heredar de la clase cl.getsoftware.jdirmonitor.AbstractTaskMonitor.

Por ejemplo, este es el código fuente de la clase cl.getsoftware.jdirmonitor.task.NormalTaskMonitor:

/*
 * JDirMonitor a listener of files
 * Copyright (C) 2010  GetSoftware Group
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * This library was written by Eugenio Contreras for GetSoftware Group
 * Contact email: contacto@getsoftare.cl
 */

package cl.getsoftware.jdirmonitor.task;

import java.io.File;

import cl.getsoftware.jdirmonitor.AbstractTaskMonitor;

/**
 * <p>Tarea de monitoreo de directorio que analiza solamente los archivos que se 
 * encuentran en el primer nivel del directorio especificado.</p>
 * 
 * @author egacl
 *
 */
public class NormalTaskMonitor extends AbstractTaskMonitor {
    
    /** (non-Javadoc)
     * @see cl.getsoftware.jdirmonitor.AbstractTaskMonitor#monitorRutine(java.io.File)
     */
    @Override
    public void monitorRutine(File directory) {
        this.normalRutine( this.getFilesOfDirectory(directory) );
    }
    
    public void normalRutine( File []files ){
        for( File file : files ){
            if( file.exists() ){
                if( file.isFile() ){
                    this.fileMonitor( file );
                }
            }
        }
    }
    
}
El método monitorRutine se ejecuta cada vez que el DirectoryMonitor inicia la rutina de monitoreo y le envía como parámetro el objeto java.io.File del directorio a analizar. Por lo tanto allí puedes escribir la manera en que deseas realizar el monitoreo.

Puedes descargar el archivo JDirMonitor.rar (proyecto Eclipse con javadoc incluida).
Api JAR JDirMonitor.jar

EJEMPLO DE COMO UTILIZAR LA API AQUI
 

27 comentarios:

egacl dijo...

Para los que descargaron la API antes del 8 de Junio, deben volver a descargar el JAR. ya que se corrigió un pequeño bug en la rutina de monitoreo.

egacl dijo...

El error se debía a que cuando recorría los listeners (los cuales están en una lista de objetos con referencia débil [WeakReference]) para notificar que había ocurrido un cambio en algún archivo, en un punto intentaba remover una weakreference de una lista lo cual me arrojaba un ConcurrentException de la clase AbstractList... y claro! no se puede modificar una lista mientras está siendo recorrida!
Pero ahora está todo solucionado ;)

egacl dijo...

Se elimina la dependencia de JDirMonitor con log4j

Roberto dijo...

Mish, interesante... pero poco elegante :P
Estar mirando si cada 5 minutos hay alguien en la puerta es poco práctico. Es mejor poner un timbre e ir a mirar cuando alguien toca el timbre. No mire que hacia MonitorBuilder, pero al parecer según el tiempo que seteas va a mirar que hay.

Si estas usando Linux, podrías usar una funcionalidad basada en el sistema de archivo (al menos se que con ext3 funciona, no se con los otros) y se llama inotify[0]. ¿Que es lo que hace? cada vez que hay un cambio (el que nosotros definamos, lectura, escritura, etc) en un directorio que tu especificas se gatilla una acción, la cual puedes recuperar desde tu aplicación con los binding respectivos para tu lenguaje favorito.

Saludos,
Roberto

[0]http://en.wikipedia.org/wiki/Inotify

egacl dijo...

@Roberto Gracias por la información. Pero la solución que propongo tiene como objetivo no amarrarse a un sistema de archivos en especial. En un principio evalué el generar interfaces que permitieran comunicarme con las APIs de sistemas de archivos de los distintos sistemas operativos, pero como dije en un principio, el desarrollo de JDirMonitor nace por un problema laboral que debe solucionarse en un tiempo acotado.
Ahora bien, con respecto a lo de poco elegante, me guardaría ese tipo de comentarios ya que cada solución depende del contexto y tipo de problema que tengas.

Anónimo dijo...

Como uso el jar??

egacl dijo...

Hola, lo primero que debes hacer es asociar el jar a tu proyecto Java y luego creas una clase que haga referencia a los recursos que provee JDirMonitor (al igual que la clase de ejemplo "Test" de este artículo).
Para asociar el jar al proyecto depende del IDE que utilices para desarrollar. Por ejemplo, si usas Eclipse debes hacer click derecho sobre el proyecto -> Propiedades -> Java Build Path -> Libraries -> Add Jars o Add External Jars.

Anónimo dijo...

Hola, esto me puede servir entonces para escuchar un directorio en particular al cual le ingresaran distintos tipos de archivos en cualquier instante y se deben procesar?

egacl dijo...

Si, esa es la idea.

Anónimo dijo...

El link para descargar el jar no funciona. de donde puedo obtener la librería?

egacl dijo...

Lo sé, tengo que arreglarlos :-/

Ahora estoy un poco ocupado con trabajo, pero los arreglaré a mas tardar hoy

Anónimo dijo...

ok, espero que puedas arreglarlo pronto así puedo bajar la librería y probarla :)
Muchas gracias!!!!

Anónimo dijo...

No se puede descargar la libreria?

egacl dijo...

envia un correo a getgeeks@gmail.com y te envio la API.
Tengo problemas con el servidor donde estan los adjuntos del blog.

egacl dijo...

Ya subí la API a Fileserve, links actualizados

egacl dijo...

Se cambian los archivos a 4shared

ccilidon dijo...

¿Como puedo hacer que procese los archivos por fecha de creación?
Felicidades!

ccilidon dijo...

Osea necesito que los procese en el orden que se fueron creando, gracias!

egacl dijo...

Hola Carlos.

JDirmonitor permite definir orden de procesamiento por fecha.

Lo que debes hacer es lo siguiente:

Cuando instancias la clase MonitorBuilder, esta posee un método que se llama "setComparator" a la cual debes pasarle como atributo un instancia de "Comparator". Esta instancia de Comparator debe obtener la fecha de la ultima modificacion de los archivos que recibe como parametro y listo!

A continuacion un ejemplo en codigo:

//instancia de monitorbuilder
MonitorBuilder builder = new MonitorBuilder(pathDirectory,
timeTaskExecute, new RecursiveTaskMonitor(),
TimeUnit.SECONDS);
//se setea el comparador
builder.setComparator(comparator);


//instancia de comparador
public static Comparator comparator = new Comparator() {
@Override
public int compare(File fileA, File fileB) {
Date dateFileA = new Date(fileA.lastModified());
Date dateFileB = new Date(fileB.lastModified());
return dateFileA.compareTo(dateFileB);
}
};



Quedo atento a tus comentarios.

Saludos.

ccilidon dijo...

Pues al parecer bien!, hice pruebas básicas y si me funcionó el código tal cual me lo enviaste.

Ahora tengo el problema que de repente deja de cachar los nuevos archivos que se van creando, tengo que reiniciar el programa para que vuelva a detectar los cambios en el directorio. Estoy corriendo en linux debian. ¿ Algún consejo que me puedas dar?

egacl dijo...

Que extraño. No debería suceder eso.
Como cuantos archivos y directorios estás procesando?
Lanza algún error la API?

ccilidon dijo...

No ningún error, simplemente se acumulan los archivos y no se procesan.
Cada archivo llega cada 10 o 15 minutos, así que no son muchos y están en un solo directorio.
¿Alguna idea de que podría ser?

Gracias por tus comentarios. Saludos.

Anónimo dijo...

Hola sabes esto es lo que necesitaba, por motivo de tiempo no podía crear algo así, ahora bien tengo la siguiente duda si quiero preguntar por la creación de un .txt en particular existe algún método de forma que me retorne algún valor booleano (existe o no existe) o debo implementarlo desde ya muchísimas gracias... Esteban

Juan dijo...

Muy buena API, hasta el momento la estoy utilizando para monitorear el ingreso de archivos a un sistema y la tengo como servicio de windows con "Java Service Wrapper" No he necesitado ni agregar ni quitar nada. :)

Anónimo dijo...

Excelente!!! Me podrías indicar como utilizarlo en un servicio de windows?

Anónimo dijo...

Estimados no se puede descargar el archivo .JAR

Luis Saenz Guerrero dijo...

egacl donde puedo descargar el archivo JAR

Publicar un comentario en la entrada

top