3333import java .util .concurrent .ConcurrentHashMap ;
3434import java .util .concurrent .Executor ;
3535import java .util .function .BiConsumer ;
36- import java .util .function .Consumer ;
3736import java .util .function .Function ;
3837
3938import org .apache .logging .log4j .LogManager ;
@@ -68,30 +67,37 @@ private class ChildWatchesUpdater implements BiConsumer<EventHandlingWatch, Watc
6867 @ Override
6968 public void accept (EventHandlingWatch watch , WatchEvent event ) {
7069 switch (event .getKind ()) {
71- case OVERFLOW :
72- reportOverflowToChildWatches ();
73- break ;
74- case CREATED :
75- var childWatch = openChildWatch (event .calculateFullPath ());
76- // Events in the newly created directory might have been
77- // missed between its creation and setting up its watch. So,
78- // generate an overflow event for the watch.
79- reportOverflowTo (childWatch );
80- break ;
81- case DELETED :
82- closeChildWatch (event .calculateFullPath ());
83- break ;
84- case MODIFIED :
85- break ;
70+ case OVERFLOW : acceptOverflow (); break ;
71+ case CREATED : acceptCreated (event .calculateFullPath ()); break ;
72+ case DELETED : acceptDeleted (event .calculateFullPath ()); break ;
73+ case MODIFIED : break ;
8674 }
8775 }
8876
89- private void reportOverflowToChildWatches () {
77+ private void acceptOverflow () {
9078 for (var childWatch : childWatches .values ()) {
9179 reportOverflowTo (childWatch );
9280 }
9381 }
9482
83+ private void acceptCreated (Path fullPath ) {
84+ if (Files .isDirectory (fullPath )) {
85+ var childWatch = openChildWatch (fullPath );
86+ // Events in the newly created directory might have been missed
87+ // between its creation and setting up its watch. So, generate
88+ // an `OVERFLOW` event for the watch.
89+ reportOverflowTo (childWatch );
90+ }
91+ }
92+
93+ private void acceptDeleted (Path fullPath ) {
94+ try {
95+ closeChildWatch (fullPath );
96+ } catch (IOException e ) {
97+ logger .error ("Could not close (nested) file tree watch for: {} ({})" , fullPath , e );
98+ }
99+ }
100+
95101 private void reportOverflowTo (EventHandlingWatch childWatch ) {
96102 var overflow = new WatchEvent (WatchEvent .Kind .OVERFLOW , childWatch .getPath ());
97103 childWatch .handleEvent (overflow );
@@ -119,22 +125,10 @@ private JDKFileTreeWatch openChildWatch(Path child) {
119125 return childWatch ;
120126 }
121127
122- private void closeChildWatch (Path child ) {
128+ private void closeChildWatch (Path child ) throws IOException {
123129 var childWatch = childWatches .remove (child );
124130 if (childWatch != null ) {
125- try {
126- childWatch .close ();
127- } catch (IOException e ) {
128- logger .error ("Could not close (nested) file tree watch for: {} ({})" , child , e );
129- }
130- }
131- }
132-
133- private void forEachChild (Consumer <Path > action ) {
134- try (var children = Files .find (path , 1 , (p , attrs ) -> p != path && attrs .isDirectory ())) {
135- children .forEach (action );
136- } catch (IOException e ) {
137- logger .error ("File tree watch (for: {}) could not iterate over its children ({})" , path , e );
131+ childWatch .close ();
138132 }
139133 }
140134
@@ -152,13 +146,45 @@ public void handleEvent(WatchEvent event) {
152146
153147 @ Override
154148 public synchronized void close () throws IOException {
155- forEachChild (this ::closeChildWatch );
156- internal .close ();
149+ IOException firstFail = null ;
150+ var children = childWatches .keySet ().iterator ();
151+ while (true ) {
152+ try {
153+ // First, close all child watches
154+ if (children .hasNext ()) {
155+ closeChildWatch (children .next ());
156+ }
157+ // Last, close the internal watch
158+ else {
159+ internal .close ();
160+ break ;
161+ }
162+ } catch (IOException ex ) {
163+ logger .error ("Could not close watch" , ex );
164+ firstFail = firstFail == null ? ex : firstFail ;
165+ } catch (Exception ex ) {
166+ logger .error ("Could not close watch" , ex );
167+ firstFail = firstFail == null ? new IOException ("Unexpected exception when closing" , ex ) : firstFail ;
168+ }
169+ }
170+ if (firstFail != null ) {
171+ throw firstFail ;
172+ }
157173 }
158174
159175 @ Override
160176 protected synchronized void start () throws IOException {
161177 internal .open ();
162- forEachChild (this ::openChildWatch );
178+ try (var children = Files .find (path , 1 , (p , attrs ) -> p != path && attrs .isDirectory ())) {
179+ children .forEach (this ::openChildWatch );
180+ } catch (IOException e ) {
181+ logger .error ("File tree watch (for: {}) could not iterate over its children ({})" , path , e );
182+ }
183+ // There's no need to report an overflow event, because `internal` was
184+ // opened *before* the file system was accessed to fetch children. Thus,
185+ // if a new directory is created while this method is running, then at
186+ // least one of the following is true: (a) the new directory is already
187+ // visible by the time the file system is accessed; (b) its `CREATED`
188+ // event is handled later, which starts a new child watch if needed.
163189 }
164190}
0 commit comments