add file follow event test

This commit is contained in:
Jörg Prante 2023-10-09 11:09:56 +02:00
parent 7bb652dba4
commit 976e29947c
6 changed files with 67 additions and 47 deletions

View file

@ -8,6 +8,8 @@ public class DefaultFileFollowEvent extends DefaultEvent implements FileFollowEv
private Path path;
private String content;
@Override
public void setPath(Path path) {
this.path = path;
@ -17,4 +19,14 @@ public class DefaultFileFollowEvent extends DefaultEvent implements FileFollowEv
public Path getPath() {
return path;
}
@Override
public void setContent(String content) {
this.content = content;
}
@Override
public String getContent() {
return content;
}
}

View file

@ -9,4 +9,8 @@ public interface FileFollowEvent extends Event {
void setPath(Path path);
Path getPath();
void setContent(String content);
String getContent();
}

View file

@ -25,7 +25,7 @@ public class FileFollowEventManager {
ExecutorService executorService,
ClassLoader classLoader) {
this.eventServiceMap = new LinkedHashMap<>();
for (Map.Entry<String, Settings> followfiles : settings.getGroups("filefollow").entrySet()) {
for (Map.Entry<String, Settings> followfiles : settings.getGroups("event.filefollow").entrySet()) {
Settings definition = followfiles.getValue();
String baseStr = definition.get("base");
String patternStr = definition.get("pattern");

View file

@ -5,6 +5,7 @@ import org.xbib.settings.Settings;
import java.io.Closeable;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SeekableByteChannel;
@ -24,13 +25,12 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
public class FileFollowEventService implements Callable<Integer>, Closeable {
private static final Logger logger = Logger.getLogger(FileFollowEventService.class.getName());
private final Settings settings;
private final EventBus eventBus;
private final Path base;
@ -41,12 +41,8 @@ public class FileFollowEventService implements Callable<Integer>, Closeable {
private final WatchService watchService;
private final WatchKey watchKey;
private final Map<Path, Long> fileSizes;
private int eventCount;
private volatile boolean keepWatching;
public FileFollowEventService(Settings settings,
@ -54,7 +50,6 @@ public class FileFollowEventService implements Callable<Integer>, Closeable {
Path base,
Pattern pattern,
Class<? extends FileFollowEvent> eventClass) throws IOException {
this.settings = settings;
this.eventBus = eventBus;
this.base = base;
this.pattern = pattern;
@ -63,9 +58,9 @@ public class FileFollowEventService implements Callable<Integer>, Closeable {
this.watchService = fileSystem.newWatchService();
WatchEvent.Kind<?>[] kinds = new WatchEvent.Kind<?>[1];
kinds[0] = StandardWatchEventKinds.ENTRY_MODIFY;
this.watchKey = base.register(watchService, kinds);
// limit file size memory to 32 files
this.fileSizes = new LimitedMap<>(32);
base.register(watchService, kinds);
this.fileSizes = new LinkedHashMap<>();
fillFileSizes(base, pattern);
this.keepWatching = true;
}
@ -85,6 +80,7 @@ public class FileFollowEventService implements Callable<Integer>, Closeable {
Path path = pathWatchEvent.context();
Matcher matcher = pattern.matcher(path.toString());
if (!matcher.matches()) {
logger.log(Level.FINE, "no match of " + path + " to pattern " + pattern);
continue;
}
Path p = base.resolve(path);
@ -92,22 +88,14 @@ public class FileFollowEventService implements Callable<Integer>, Closeable {
long lastSize = fileSizes.getOrDefault(p, 0L);
long currentSize = p.toFile().length();
fileSizes.put(p, currentSize);
// We have no idea where to start reading if this is the first time.
// Avoid reading the whole file at first time, read only real diff.
// This means first event is swallowed!
if (lastSize > 0L) {
String content = readRange(channel, lastSize, currentSize);
// split content by line, this allows pattern matching without preprocessing in worker
for (String line : content.split("\n")) {
FileFollowEvent event = eventClass.getDeclaredConstructor().newInstance();
event.setKey(path.toString());
event.setPath(base);
event.setMap(new LinkedHashMap<>());
event.getMap().putAll(settings.getAsStructuredMap());
event.getMap().put("content", line);
eventBus.post(event);
eventCount++;
}
String content = readRange(channel, lastSize, currentSize);
// split content by line, this allows pattern matching without preprocessing in worker
for (String line : content.split("\n")) {
FileFollowEvent event = eventClass.getDeclaredConstructor().newInstance();
event.setKey(base.toString());
event.setPath(path);
event.setContent(line);
eventBus.post(event);
}
}
}
@ -121,7 +109,7 @@ public class FileFollowEventService implements Callable<Integer>, Closeable {
}
}
}
return eventCount;
return 0;
}
@Override
@ -138,26 +126,29 @@ public class FileFollowEventService implements Callable<Integer>, Closeable {
fileChannel.position(from);
int numRead = fileChannel.read(byteBuffer);
byteBuffer.flip();
CharBuffer chb = StandardCharsets.UTF_8.decode(byteBuffer);
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(byteBuffer);
byteBuffer.clear();
if (numRead <= 0) {
throw new IOException("numRead less or equal to 0");
}
return chb.toString();
return charBuffer.toString();
}
private static class LimitedMap<K, V> extends LinkedHashMap<K, V> {
private final int n;
LimitedMap(int n) {
super();
this.n = n;
private void fillFileSizes(Path base, Pattern pattern) throws IOException {
if (!Files.exists(base)) {
return;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > n;
try (Stream<Path> path = Files.walk(base)) {
path.forEach(p -> {
Matcher matcher = pattern.matcher(p.toString());
if (matcher.matches()) {
try {
fileSizes.put(p, Files.size(p));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
}
}
}

View file

@ -6,27 +6,40 @@ import org.xbib.event.timer.TestTimerEventConsumer;
import org.xbib.event.timer.TimerEventManager;
import org.xbib.settings.Settings;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FileFollowEventManagerTest {
private static final Logger logger = Logger.getLogger(FileFollowEventManagerTest.class.getName());
@Test
public void testFileFollowEvents() throws IOException, InterruptedException {
Path path = Files.createTempDirectory("testfilefollow");
TestFileFollowEventConsumer consumer = new TestFileFollowEventConsumer();
EventManager.register(consumer);
Settings settings = Settings.settingsBuilder()
.put("event.timer.testfilefollowevent.enabled", "true")
.put("event.timer.testfilefollowevent.class", "org.xbib.event.io.file.TestFileFollowEvent")
.put("event.timer.testfilefollowevent.path", path.toString())
.put("event.filefollow.testfilefollowevent.enabled", "true")
.put("event.filefollow.testfilefollowevent.class", "org.xbib.event.io.file.TestFileFollowEvent")
.put("event.filefollow.testfilefollowevent.base", path.toString())
.put("event.filefollow.testfilefollowevent.pattern", ".*")
.build();
EventManager eventManager = EventManager.newEventManager(settings);
FileFollowEventManager fileFolloeEventManager = eventManager.getFileFollowEventManager()
Thread.sleep(10000L);
FileFollowEventManager fileFolloeEventManager = eventManager.getFileFollowEventManager();
Thread.sleep(5000L);
try (BufferedWriter bufferedWriter = Files.newBufferedWriter(path.resolve("test.txt"))) {
bufferedWriter.write("Hello");
logger.log(Level.INFO, "Hello written");
}
Thread.sleep(5000L);
fileFolloeEventManager.close();
Files.delete(path.resolve("test.txt"));
Files.delete(path);
}
}

View file

@ -16,6 +16,6 @@ public class TestFileFollowEventConsumer implements EventConsumer {
@Subscribe
@AllowConcurrentEvents
void onEvent(TestFileFollowEvent event) {
logger.log(Level.INFO, "received filefollw event path = " + event.getPath());
logger.log(Level.INFO, "received filefollw event path = " + event.getPath() + " content = " + event.getContent());
}
}