Add SIN.04028 tutorial material, tutor prompt and conversation log
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.ClosedWatchServiceException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.nio.file.WatchService;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
record IncomingMessage(String mailbox, Message message) {}
|
||||
|
||||
final class FileSubscriptions implements AutoCloseable {
|
||||
private final WatchService watcher;
|
||||
private final Map<String, WatchKey> subscriptions = new ConcurrentHashMap<>();
|
||||
private final Map<WatchKey, String> mailboxes = new ConcurrentHashMap<>();
|
||||
private final BlockingQueue<IncomingMessage> messages =
|
||||
new LinkedBlockingQueue<>();
|
||||
private final Thread observer;
|
||||
|
||||
FileSubscriptions() throws IOException {
|
||||
watcher = FileSystems.getDefault().newWatchService();
|
||||
observer = Thread.ofVirtual().start(this::observe);
|
||||
}
|
||||
|
||||
void subscribe(String mailbox) throws IOException {
|
||||
if (subscriptions.containsKey(mailbox))
|
||||
return;
|
||||
|
||||
var directory = FileMessageBoard.directory(mailbox);
|
||||
Files.createDirectories(directory);
|
||||
var key = directory.register(
|
||||
watcher,
|
||||
StandardWatchEventKinds.ENTRY_CREATE
|
||||
);
|
||||
|
||||
subscriptions.put(mailbox, key);
|
||||
mailboxes.put(key, mailbox);
|
||||
}
|
||||
|
||||
void unsubscribe(String mailbox) {
|
||||
var key = subscriptions.remove(mailbox);
|
||||
|
||||
if (key != null) {
|
||||
mailboxes.remove(key);
|
||||
key.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
IncomingMessage take() throws InterruptedException {
|
||||
return messages.take();
|
||||
}
|
||||
|
||||
private void observe() {
|
||||
try {
|
||||
while (true) {
|
||||
var key = watcher.take();
|
||||
var mailbox = mailboxes.get(key);
|
||||
|
||||
if (mailbox == null) {
|
||||
key.reset();
|
||||
continue;
|
||||
}
|
||||
|
||||
var directory = FileMessageBoard.directory(mailbox);
|
||||
|
||||
for (var event : key.pollEvents()) {
|
||||
if (event.kind() == StandardWatchEventKinds.OVERFLOW)
|
||||
continue;
|
||||
|
||||
var filename = (Path) event.context();
|
||||
|
||||
if (!filename.toString().endsWith(".msg"))
|
||||
continue;
|
||||
|
||||
var file = directory.resolve(filename);
|
||||
|
||||
if (Files.isRegularFile(file))
|
||||
messages.put(
|
||||
new IncomingMessage(mailbox, FileMessageBoard.read(file))
|
||||
);
|
||||
}
|
||||
|
||||
if (!key.reset()) {
|
||||
subscriptions.remove(mailbox);
|
||||
mailboxes.remove(key);
|
||||
}
|
||||
}
|
||||
} catch (ClosedWatchServiceException _) {
|
||||
// close() ends the observer.
|
||||
} catch (InterruptedException _) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (IOException exception) {
|
||||
throw new UncheckedIOException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
watcher.close();
|
||||
observer.interrupt();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user