diff --git a/src/main/java/eu/ztsh/lfr/files/FileModifiedEvent.java b/src/main/java/eu/ztsh/lfr/files/FileModifiedEvent.java new file mode 100644 index 0000000..332b898 --- /dev/null +++ b/src/main/java/eu/ztsh/lfr/files/FileModifiedEvent.java @@ -0,0 +1,5 @@ +package eu.ztsh.lfr.files; + +public record FileModifiedEvent(long timestamp) { + +} diff --git a/src/main/java/eu/ztsh/lfr/files/WatcherService.java b/src/main/java/eu/ztsh/lfr/files/WatcherService.java new file mode 100644 index 0000000..c021211 --- /dev/null +++ b/src/main/java/eu/ztsh/lfr/files/WatcherService.java @@ -0,0 +1,76 @@ +package eu.ztsh.lfr.files; + +import eu.ztsh.lfr.config.DataProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Paths; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; + +@Service +public class WatcherService { + + private static final Logger log = LoggerFactory.getLogger(WatcherService.class); + + private final WatchService watchService; + private final ApplicationEventPublisher eventPublisher; + + private final String fileName; + + @Autowired + public WatcherService(DataProperties dataProperties, ApplicationEventPublisher eventPublisher) throws IOException { + this.eventPublisher = eventPublisher; + this.watchService = FileSystems.getDefault().newWatchService(); + + var filePath = Paths.get(dataProperties.fileUrl()); + fileName = filePath.getFileName().toString(); + filePath.getParent().register( + watchService, + // register file creation as some editors modify files in temp, and then replaces original file. + StandardWatchEventKinds.ENTRY_CREATE, + StandardWatchEventKinds.ENTRY_MODIFY + ); + } + + @SuppressWarnings("BusyWait") + @EventListener(ApplicationReadyEvent.class) + public void watch() throws InterruptedException { + WatchKey key; + while ((key = watchService.take()) != null) { + /* + When modifying in IntelliJ, one line edit ends with: + ENTRY_CREATE: temperatures.csv~ + ENTRY_MODIFY: temperatures.csv~ + ENTRY_MODIFY: temperatures.csv~ + ENTRY_MODIFY: temperatures.csv + ENTRY_MODIFY: temperatures.csv + ENTRY_MODIFY: temperatures.csv + Thread.sleep is used to filter out duplicated events. + origin: https://stackoverflow.com/a/25221600 + */ + Thread.sleep(50); + var maybeLastEvent = key.pollEvents().stream() + .filter(event -> event.context() != null) + .filter(event -> event.context().toString().endsWith(fileName)) + .reduce((o1, o2) -> o2); + if (maybeLastEvent.isPresent()) { + var lastEvent = maybeLastEvent.get(); + log.info("Got event of kind:{}", lastEvent.kind()); + eventPublisher.publishEvent(new FileModifiedEvent(System.currentTimeMillis())); + } else { + log.trace("Got event for not watched file"); + } + key.reset(); + } + } + +}