diff --git a/ws-api-watch-dir/.gitignore b/ws-api-watch-dir/.gitignore
new file mode 100644
index 0000000..33653da
--- /dev/null
+++ b/ws-api-watch-dir/.gitignore
@@ -0,0 +1,31 @@
+
+# Files
+.classpath
+.externalToolBuilders
+.gradle
+.project
+.pydevproject
+.settings
+.sonar
+*~
+*.ipr
+*.iml
+*.iws
+*.swp
+*.DS_Store
+*.snap.debug
+dependency-reduced-pom.xml
+
+# Directories
+.idea/
+.run/
+.venv/
+_site/
+build/
+dist/
+docs/_build/
+gen-java/
+gen-javabean/
+gen-py/
+node_modules/
+target/
diff --git a/ws-api-watch-dir/pom.xml b/ws-api-watch-dir/pom.xml
new file mode 100644
index 0000000..a60ebfd
--- /dev/null
+++ b/ws-api-watch-dir/pom.xml
@@ -0,0 +1,86 @@
+
+
+ 4.0.0
+
+ watch_dir
+ description
+ https://swingbe.de
+ de.swingbe
+ watch_dir
+ 0.0.1
+ jar
+
+
+
+ GNU General Public License
+ https://www.gnu.org/licenses/gpl-3.0.txt
+
+
+
+
+ https://github.com/Software-Ingenieur-Begerad/sandbox-java
+
+
+
+
+ UTF-8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 11
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.1
+
+
+ package
+
+ shade
+
+
+
+
+
+ *:*
+
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+
+
+
+
+ true
+
+ shaded
+
+
+
+
+ de.swingbe.watch_dir.Main
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ws-api-watch-dir/src/main/java/de/swingbe/watch_dir/Main.java b/ws-api-watch-dir/src/main/java/de/swingbe/watch_dir/Main.java
new file mode 100644
index 0000000..81538d0
--- /dev/null
+++ b/ws-api-watch-dir/src/main/java/de/swingbe/watch_dir/Main.java
@@ -0,0 +1,166 @@
+package de.swingbe.watch_dir;
+
+import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
+import static java.nio.file.StandardWatchEventKinds.*;
+
+/**
+ * Example to watch a directory (or tree) for changes to files.
+ */
+
+public class Main {
+
+ private final WatchService watcher;
+ private final Map keys;
+ private final boolean recursive;
+ private boolean trace = false;
+
+ @SuppressWarnings("unchecked")
+ static WatchEvent cast(WatchEvent> event) {
+ return (WatchEvent) event;
+ }
+
+ /**
+ * Register the given directory with the WatchService
+ */
+ private void register(Path dir) throws IOException {
+ WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
+ if (trace) {
+ Path prev = keys.get(key);
+ if (prev == null) {
+ System.out.format("register: %s\n", dir);
+ } else {
+ if (!dir.equals(prev)) {
+ System.out.format("update: %s -> %s\n", prev, dir);
+ }
+ }
+ }
+ keys.put(key, dir);
+ }
+
+ /**
+ * Register the given directory, and all its sub-directories, with the
+ * WatchService.
+ */
+ private void registerAll(final Path start) throws IOException {
+ // register directory and sub-directories
+ Files.walkFileTree(start, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+ throws IOException {
+ register(dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ /**
+ * Creates a WatchService and registers the given directory
+ */
+ Main(Path dir, boolean recursive) throws IOException {
+ this.watcher = FileSystems.getDefault().newWatchService();
+ this.keys = new HashMap();
+ this.recursive = recursive;
+
+ if (recursive) {
+ System.out.format("Scanning %s ...\n", dir);
+ registerAll(dir);
+ System.out.println("Done.");
+ } else {
+ register(dir);
+ }
+
+ // enable trace after initial registration
+ this.trace = true;
+ }
+
+ /**
+ * Process all events for keys queued to the watcher
+ */
+ void processEvents() {
+ for (; ; ) {
+
+ // wait for key to be signalled
+ WatchKey key;
+ try {
+ key = watcher.take();
+ } catch (InterruptedException x) {
+ return;
+ }
+
+ Path dir = keys.get(key);
+ if (dir == null) {
+ System.err.println("WatchKey not recognized!!");
+ continue;
+ }
+
+ for (WatchEvent> event : key.pollEvents()) {
+ WatchEvent.Kind kind = event.kind();
+
+ // TBD - provide example of how OVERFLOW event is handled
+ if (kind == OVERFLOW) {
+ continue;
+ }
+
+ // Context for directory entry event is the file name of entry
+ WatchEvent ev = cast(event);
+ Path name = ev.context();
+ Path child = dir.resolve(name);
+
+ // print out event
+ System.out.format("%s: %s\n", event.kind().name(), child);
+
+ // if directory is created, and watching recursively, then
+ // register it and its sub-directories
+ if (recursive && (kind == ENTRY_CREATE)) {
+ try {
+ if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
+ registerAll(child);
+ }
+ } catch (IOException x) {
+ // ignore to keep sample readbale
+ }
+ }
+ }
+
+ // reset key and remove from set if directory no longer accessible
+ boolean valid = key.reset();
+ if (!valid) {
+ keys.remove(key);
+
+ // all directories are inaccessible
+ if (keys.isEmpty()) {
+ break;
+ }
+ }
+ }
+ }
+
+ static void usage() {
+ System.err.println("usage: java Main [-r] dir");
+ System.exit(-1);
+ }
+
+ public static void main(String[] args) throws IOException {
+ // parse arguments
+ if (args.length == 0 || args.length > 2)
+ usage();
+ boolean recursive = false;
+ int dirArg = 0;
+ if (args[0].equals("-r")) {
+ if (args.length < 2)
+ usage();
+ recursive = true;
+ dirArg++;
+ }
+
+ // register directory and process its events
+ Path dir = Paths.get(args[dirArg]);
+ new Main(dir, recursive).processEvents();
+ }
+}