2929import java .io .IOException ;
3030import java .nio .file .Files ;
3131import java .nio .file .Path ;
32+ import java .util .HashSet ;
3233import java .util .Map ;
3334import java .util .concurrent .ConcurrentHashMap ;
3435import java .util .concurrent .Executor ;
@@ -97,22 +98,25 @@ protected WatchEvent translate(java.nio.file.WatchEvent<?> jdkEvent) {
9798 }
9899
99100 /**
100- * Event handler that updates the child watches according to the following
101- * rules: (a) when an overflow happens, it's propagated to each existing
102- * child watch; (b) when a subdirectory creation happens, a new child watch
103- * is opened for that subdirectory; (c) when a subdirectory deletion
104- * happens, an existing child watch is closed for that subdirectory.
101+ * Event handler that asynchronously (using {@link JDKBaseWatch#exec})
102+ * updates the child watches according to the following rules: (a) when an
103+ * overflow happens, the directory is rescanned, new child watches for
104+ * created subdirectories are opened, existing child watches for deleted
105+ * subdirectories are closed, and the overflow is propagated to each child
106+ * watch; (b) when a subdirectory creation happens, a new child watch is
107+ * opened for that subdirectory; (c) when a subdirectory deletion happens,
108+ * an existing child watch is closed for that subdirectory.
105109 */
106110 private class AsyncChildWatchesUpdater implements BiConsumer <EventHandlingWatch , WatchEvent > {
107111 @ Override
108112 public void accept (EventHandlingWatch watch , WatchEvent event ) {
109113 exec .execute (() -> {
110- switch (event .getKind ()) {
111- case OVERFLOW : acceptOverflow (); break ;
112- case CREATED : getFileNameAndThen (event , this ::acceptCreated ); break ;
113- case DELETED : getFileNameAndThen (event , this ::acceptDeleted ); break ;
114- case MODIFIED : break ;
115- }
114+ switch (event .getKind ()) {
115+ case OVERFLOW : acceptOverflow (); break ;
116+ case CREATED : getFileNameAndThen (event , this ::acceptCreated ); break ;
117+ case DELETED : getFileNameAndThen (event , this ::acceptDeleted ); break ;
118+ case MODIFIED : break ;
119+ }
116120 });
117121 }
118122
@@ -126,7 +130,7 @@ private void getFileNameAndThen(WatchEvent event, Consumer<Path> consumer) {
126130 }
127131
128132 private void acceptOverflow () {
129- openChildWatches ();
133+ openAndCloseChildWatches ();
130134 for (var childWatch : childWatches .values ()) {
131135 reportOverflowTo (childWatch );
132136 }
@@ -143,11 +147,7 @@ private void acceptCreated(Path child) {
143147 }
144148
145149 private void acceptDeleted (Path child ) {
146- try {
147- closeChildWatch (child );
148- } catch (IOException e ) {
149- logger .error ("Could not close (nested) file tree watch for: {} ({})" , path .resolve (child ), e );
150- }
150+ tryCloseChildWatch (child );
151151 }
152152
153153 private void reportOverflowTo (JDKFileTreeWatch childWatch ) {
@@ -157,11 +157,14 @@ private void reportOverflowTo(JDKFileTreeWatch childWatch) {
157157 }
158158 }
159159
160- private void openChildWatches () {
160+ private void openAndCloseChildWatches () {
161+ var toBeClosed = new HashSet <>(childWatches .keySet ());
162+
161163 try (var children = Files .find (path , 1 , (p , attrs ) -> p != path && attrs .isDirectory ())) {
162164 children .forEach (p -> {
163165 var child = p .getFileName ();
164166 if (child != null ) {
167+ toBeClosed .remove (child );
165168 openChildWatch (child );
166169 } else {
167170 logger .error ("File tree watch (for: {}) could not open a child watch for: {}" , path , p );
@@ -170,6 +173,10 @@ private void openChildWatches() {
170173 } catch (IOException e ) {
171174 logger .error ("File tree watch (for: {}) could not iterate over its children ({})" , path , e );
172175 }
176+
177+ for (var child : toBeClosed ) {
178+ tryCloseChildWatch (child );
179+ }
173180 }
174181
175182 private JDKFileTreeWatch openChildWatch (Path child ) {
@@ -186,6 +193,14 @@ private JDKFileTreeWatch openChildWatch(Path child) {
186193 return childWatch ;
187194 }
188195
196+ private void tryCloseChildWatch (Path child ) {
197+ try {
198+ closeChildWatch (child );
199+ } catch (IOException e ) {
200+ logger .error ("Could not close (nested) file tree watch for: {} ({})" , path .resolve (child ), e );
201+ }
202+ }
203+
189204 private void closeChildWatch (Path child ) throws IOException {
190205 assert !child .isAbsolute ();
191206
@@ -242,7 +257,7 @@ public synchronized void close() throws IOException {
242257 @ Override
243258 protected synchronized void start () throws IOException {
244259 internal .open ();
245- openChildWatches ();
260+ openAndCloseChildWatches ();
246261 // There's no need to report an overflow event, because `internal` was
247262 // opened *before* the file system was accessed to fetch children. Thus,
248263 // if a new directory is created while this method is running, then at
0 commit comments