Vigilante del buzón de entrada de denuncias
Detectar automáticamente, sin polling manual, los nuevos ficheros de captación que depositan los radares en buzon-entrada/, usando WatchService para que el sistema operativo notifique los cambios.
1Registrar el directorio a vigilar
WatchService se suscribe a eventos del sistema de ficheros: en vez de comprobar el directorio en bucle, el SO avisa cuando cambia algo.
Path directorio = Path.of("buzon-entrada");
WatchService watcher = FileSystems.getDefault().newWatchService();
directorio.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
2Atender el evento ENTRY_CREATE
Solo interesa la creación de ficheros nuevos, no su modificación ni su borrado.
| Evento | Cuándo se dispara |
|---|---|
ENTRY_CREATE | Se crea un fichero o subdirectorio — el que usa esta actividad |
ENTRY_MODIFY | Se modifica el contenido de una entrada |
ENTRY_DELETE | Se elimina una entrada |
3Bucle de procesamiento y reset()
Cada WatchKey debe reiniciarse tras procesar sus eventos. Olvidarlo hace que «el vigilante deje de funcionar en silencio»: sin excepción visible, simplemente deja de recibir eventos.
while (activo) {
WatchKey clave = watcher.take(); // bloquea hasta que hay eventos
for (WatchEvent<?> evento : clave.pollEvents()) {
if (evento.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
Path rutaCompleta = directorio.resolve((Path) evento.context());
log.info("Fichero detectado: {}", rutaCompleta);
procesar(rutaCompleta);
ultimoDetectado = rutaCompleta;
}
}
boolean sigueValida = clave.reset(); // obligatorio para seguir recibiendo eventos
if (!sigueValida) {
break;
}
}
4take() vs. poll(timeout)
Decide y documenta cuál usar: no es una elección neutra.
| Estrategia | Comportamiento | Cuándo conviene |
|---|---|---|
watcher.take() | Bloquea indefinidamente hasta que hay eventos | Hilo dedicado en exclusiva a vigilar |
watcher.poll(timeout, unidad) | Devuelve enseguida o espera como máximo el timeout | El hilo necesita atender otras tareas periódicas además de vigilar |
5Ciclo de vida: hilo propio, arranque y parada
El vigilante vive en su propio hilo, arrancado al iniciar la aplicación y detenido de forma ordenada al pararla — no debe quedar un hilo colgado.
@Component
public class VigilanteBuzonEntrada {
private Thread hilo;
private volatile boolean activo;
@PostConstruct
void iniciar() {
activo = true;
hilo = new Thread(this::vigilar, "vigilante-buzon");
hilo.start();
}
@PreDestroy
void detener() throws InterruptedException {
activo = false;
hilo.interrupt();
hilo.join();
}
}
6Endpoint de estado
@GetMapping("/buzon-entrada/estado")
public EstadoBuzon estado() {
return new EstadoBuzon(vigilante.isActivo(), vigilante.getUltimoDetectado());
}
!Problemas frecuentes
clave.reset(), el vigilante deja de recibir eventos sin lanzar ningún error: parece que funciona pero está muerto.WatchService (o úsalo con try-with-resources si el ciclo de vida lo permite) al detener la aplicación para no dejar recursos del SO abiertos.✓Criterios de aceptación
- El registro se hace sobre el directorio correcto al arrancar
- La detección ocurre en un tiempo razonable
- Se llama a
reset()de laWatchKeytras procesar los eventos - La detención es ordenada, sin hilos colgados
- La decisión entre
take()/poll()está justificada en el diario técnico - El endpoint refleja correctamente el estado activo y el último fichero detectado
Adaptado de apartado d — WatchService y apartado h — Actividades.