Skip to content

Commit 71ac833

Browse files
committed
Improve code quality of JDKFileTreeWatch
1 parent 4aed091 commit 71ac833

File tree

2 files changed

+64
-38
lines changed

2 files changed

+64
-38
lines changed

src/main/java/engineering/swat/watch/impl/jdk/JDKFileTreeWatch.java

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import java.util.concurrent.ConcurrentHashMap;
3434
import java.util.concurrent.Executor;
3535
import java.util.function.BiConsumer;
36-
import java.util.function.Consumer;
3736
import java.util.function.Function;
3837

3938
import 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
}

src/main/java/engineering/swat/watch/impl/jdk/JDKRecursiveDirectoryWatch.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ private void handleCreate(WatchEvent ev) {
7676
try {
7777
if (Files.isDirectory(fullPath)) {
7878
addNewDirectory(fullPath);
79-
triggerOverflow(fullPath);
79+
reportOverflow(fullPath);
8080
}
8181
} catch (IOException ex) {
8282
logger.error("Could not locate new sub directories for: {}", ev.calculateFullPath(), ex);
@@ -87,7 +87,7 @@ private void handleCreate(WatchEvent ev) {
8787
private void handleOverflow(WatchEvent ev) {
8888
var fullPath = ev.calculateFullPath();
8989
try (var children = Files.find(fullPath, 1, (p, attrs) -> p != fullPath && attrs.isDirectory())) {
90-
children.forEach(JDKRecursiveDirectoryWatch.this::triggerOverflow);
90+
children.forEach(JDKRecursiveDirectoryWatch.this::reportOverflow);
9191
} catch (IOException e) {
9292
logger.error("Could not handle overflow for: {} ({})", fullPath, e);
9393
}
@@ -158,10 +158,10 @@ private BiConsumer<EventHandlingWatch, WatchEvent> relocater(Path subRoot) {
158158

159159
private void registerInitialWatches(Path dir) throws IOException {
160160
Files.walkFileTree(dir, new InitialDirectoryScan(dir));
161-
triggerOverflow(dir);
161+
reportOverflow(dir);
162162
}
163163

164-
private void triggerOverflow(Path p) {
164+
private void reportOverflow(Path p) {
165165
var w = activeWatches.get(p);
166166
if (w != null) {
167167
var overflow = new WatchEvent(WatchEvent.Kind.OVERFLOW, p);

0 commit comments

Comments
 (0)