28
28
import java .nio .file .WatchKey ;
29
29
import java .nio .file .WatchService ;
30
30
import java .time .Duration ;
31
+ import java .util .Collections ;
31
32
import java .util .HashSet ;
32
33
import java .util .List ;
33
34
import java .util .Map ;
@@ -85,71 +86,75 @@ void watch(Set<Path> paths, Runnable action) {
85
86
this .thread = new WatcherThread ();
86
87
this .thread .start ();
87
88
}
88
- Set <Path > registrationPaths = new HashSet <>();
89
- for (Path path : paths ) {
90
- registrationPaths .addAll (getRegistrationPaths (path ));
91
- }
92
- this .thread .register (new Registration (registrationPaths , action ));
89
+ this .thread .register (new Registration (getRegistrationPaths (paths ), action ));
93
90
}
94
91
catch (IOException ex ) {
95
92
throw new UncheckedIOException ("Failed to register paths for watching: " + paths , ex );
96
93
}
97
94
}
98
95
}
99
96
100
- @ Override
101
- public void close () throws IOException {
102
- synchronized (this .lock ) {
103
- if (this .thread != null ) {
104
- this .thread .close ();
105
- this .thread .interrupt ();
106
- try {
107
- this .thread .join ();
108
- }
109
- catch (InterruptedException ex ) {
110
- Thread .currentThread ().interrupt ();
111
- }
112
- this .thread = null ;
113
- }
114
- }
115
- }
116
-
117
97
/**
118
98
* Retrieves all {@link Path Paths} that should be registered for the specified
119
99
* {@link Path}. If the path is a symlink, changes to the symlink should be monitored,
120
100
* not just the file it points to. For example, for the given {@code keystore.jks}
121
101
* path in the following directory structure:<pre>
122
- * .
123
- * ├── ..a72e81ff-f0e1-41d8-a19b-068d3d1d4e2f
124
- * │ ├── keystore.jks
125
- * ├── ..data -> ..a72e81ff-f0e1-41d8-a19b-068d3d1d4e2f
126
- * ├── keystore.jks -> ..data/keystore.jks
102
+ * +- stores
103
+ * | +─ keystore.jks
104
+ * +- <em>data</em> -> stores
105
+ * +─ <em>keystore.jks</em> -> data/keystore.jks
127
106
* </pre> the resulting paths would include:
107
+ * <p>
128
108
* <ul>
129
- * <li><b> keystore.jks</b> </li>
130
- * <li><b>.. data/keystore.jks</b> </li>
131
- * <li><b>.. data</b> </li>
132
- * <li><b>..a72e81ff-f0e1-41d8-a19b-068d3d1d4e2f /keystore.jks</b> </li>
109
+ * <li>{@code keystore.jks} </li>
110
+ * <li>{@code data/keystore.jks} </li>
111
+ * <li>{@code data} </li>
112
+ * <li>{@code stores /keystore.jks} </li>
133
113
* </ul>
134
- * @param path the path
114
+ * @param paths the source paths
135
115
* @return all possible {@link Path} instances to be registered
136
116
* @throws IOException if an I/O error occurs
137
117
*/
138
- private static Set <Path > getRegistrationPaths (Path path ) throws IOException {
139
- path = path .toAbsolutePath ();
118
+ private Set <Path > getRegistrationPaths (Set <Path > paths ) throws IOException {
140
119
Set <Path > result = new HashSet <>();
120
+ for (Path path : paths ) {
121
+ collectRegistrationPaths (path , result );
122
+ }
123
+ return Collections .unmodifiableSet (result );
124
+ }
125
+
126
+ private void collectRegistrationPaths (Path path , Set <Path > result ) throws IOException {
127
+ path = path .toAbsolutePath ();
141
128
result .add (path );
142
129
Path parent = path .getParent ();
143
130
if (parent != null && Files .isSymbolicLink (parent )) {
144
131
result .add (parent );
145
- Path target = parent .resolveSibling (Files .readSymbolicLink (parent ));
146
- result .addAll (getRegistrationPaths (target .resolve (path .getFileName ())));
132
+ collectRegistrationPaths (resolveSiblingSymbolicLink (parent ).resolve (path .getFileName ()), result );
147
133
}
148
134
else if (Files .isSymbolicLink (path )) {
149
- Path target = path .resolveSibling (Files .readSymbolicLink (path ));
150
- result .addAll (getRegistrationPaths (target ));
135
+ collectRegistrationPaths (resolveSiblingSymbolicLink (path ), result );
136
+ }
137
+ }
138
+
139
+ private Path resolveSiblingSymbolicLink (Path path ) throws IOException {
140
+ return path .resolveSibling (Files .readSymbolicLink (path ));
141
+ }
142
+
143
+ @ Override
144
+ public void close () throws IOException {
145
+ synchronized (this .lock ) {
146
+ if (this .thread != null ) {
147
+ this .thread .close ();
148
+ this .thread .interrupt ();
149
+ try {
150
+ this .thread .join ();
151
+ }
152
+ catch (InterruptedException ex ) {
153
+ Thread .currentThread ().interrupt ();
154
+ }
155
+ this .thread = null ;
156
+ }
151
157
}
152
- return result ;
153
158
}
154
159
155
160
/**
@@ -254,6 +259,9 @@ public void close() throws IOException {
254
259
255
260
/**
256
261
* An individual watch registration.
262
+ *
263
+ * @param paths the paths being registered
264
+ * @param action the action to take
257
265
*/
258
266
private record Registration (Set <Path > paths , Runnable action ) {
259
267
@@ -265,6 +273,7 @@ boolean manages(Path file) {
265
273
private boolean isInDirectories (Path file ) {
266
274
return this .paths .stream ().filter (Files ::isDirectory ).anyMatch (file ::startsWith );
267
275
}
276
+
268
277
}
269
278
270
279
}
0 commit comments