Skip to content

Commit cc1e232

Browse files
committed
Merge branch '3.4.x'
2 parents 62b88dc + cf39de4 commit cc1e232

File tree

2 files changed

+60
-51
lines changed
  • spring-boot-project/spring-boot-autoconfigure/src

2 files changed

+60
-51
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/ssl/FileWatcher.java

+48-39
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.nio.file.WatchKey;
2929
import java.nio.file.WatchService;
3030
import java.time.Duration;
31+
import java.util.Collections;
3132
import java.util.HashSet;
3233
import java.util.List;
3334
import java.util.Map;
@@ -85,71 +86,75 @@ void watch(Set<Path> paths, Runnable action) {
8586
this.thread = new WatcherThread();
8687
this.thread.start();
8788
}
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));
9390
}
9491
catch (IOException ex) {
9592
throw new UncheckedIOException("Failed to register paths for watching: " + paths, ex);
9693
}
9794
}
9895
}
9996

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-
11797
/**
11898
* Retrieves all {@link Path Paths} that should be registered for the specified
11999
* {@link Path}. If the path is a symlink, changes to the symlink should be monitored,
120100
* not just the file it points to. For example, for the given {@code keystore.jks}
121101
* 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> -&gt; stores
105+
* +─ <em>keystore.jks</em> -&gt; data/keystore.jks
127106
* </pre> the resulting paths would include:
107+
* <p>
128108
* <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>
133113
* </ul>
134-
* @param path the path
114+
* @param paths the source paths
135115
* @return all possible {@link Path} instances to be registered
136116
* @throws IOException if an I/O error occurs
137117
*/
138-
private static Set<Path> getRegistrationPaths(Path path) throws IOException {
139-
path = path.toAbsolutePath();
118+
private Set<Path> getRegistrationPaths(Set<Path> paths) throws IOException {
140119
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();
141128
result.add(path);
142129
Path parent = path.getParent();
143130
if (parent != null && Files.isSymbolicLink(parent)) {
144131
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);
147133
}
148134
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+
}
151157
}
152-
return result;
153158
}
154159

155160
/**
@@ -254,6 +259,9 @@ public void close() throws IOException {
254259

255260
/**
256261
* An individual watch registration.
262+
*
263+
* @param paths the paths being registered
264+
* @param action the action to take
257265
*/
258266
private record Registration(Set<Path> paths, Runnable action) {
259267

@@ -265,6 +273,7 @@ boolean manages(Path file) {
265273
private boolean isInDirectories(Path file) {
266274
return this.paths.stream().filter(Files::isDirectory).anyMatch(file::startsWith);
267275
}
276+
268277
}
269278

270279
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/ssl/FileWatcherTests.java

+12-12
Original file line numberDiff line numberDiff line change
@@ -259,24 +259,24 @@ void shouldTriggerOnConfigMapUpdates(@TempDir Path tempDir) throws Exception {
259259
/**
260260
* Updates many times K8s ConfigMap/Secret with atomic move. <pre>
261261
* .
262-
* ├── ..a72e81ff-f0e1-41d8-a19b-068d3d1d4e2f
263-
* │ ├── keystore.jks
264-
* ├── ..data -> ..a72e81ff-f0e1-41d8-a19b-068d3d1d4e2f
265-
* ├── keystore.jks -> ..data/keystore.jks
262+
* +─ ..a72e81ff-f0e1-41d8-a19b-068d3d1d4e2f
263+
* │ +─ keystore.jks
264+
* +─ ..data -&gt; ..a72e81ff-f0e1-41d8-a19b-068d3d1d4e2f
265+
* +─ keystore.jks -&gt; ..data/keystore.jks
266266
* </pre>
267267
*
268268
* After a first a ConfigMap/Secret update, this will look like: <pre>
269269
* .
270-
* ├── ..bba2a61f-ce04-4c35-93aa-e455110d4487
271-
* │ ├── keystore.jks
272-
* ├── ..data -> ..bba2a61f-ce04-4c35-93aa-e455110d4487
273-
* ├── keystore.jks -> ..data/keystore.jks
270+
* +─ ..bba2a61f-ce04-4c35-93aa-e455110d4487
271+
* │ +─ keystore.jks
272+
* +─ ..data -&gt; ..bba2a61f-ce04-4c35-93aa-e455110d4487
273+
* +─ keystore.jks -&gt; ..data/keystore.jks
274274
* </pre> After a second a ConfigMap/Secret update, this will look like: <pre>
275275
* .
276-
* ├── ..134887f0-df8f-4433-b70c-7784d2a33bd1
277-
* │ ├── keystore.jks
278-
* ├── ..data -> ..134887f0-df8f-4433-b70c-7784d2a33bd1
279-
* ├── keystore.jks -> ..data/keystore.jks
276+
* +─ ..134887f0-df8f-4433-b70c-7784d2a33bd1
277+
* │ +─ keystore.jks
278+
* +─ ..data -&gt; ..134887f0-df8f-4433-b70c-7784d2a33bd1
279+
* +─ keystore.jks -&gt; ..data/keystore.jks
280280
*</pre>
281281
* <p>
282282
* When Kubernetes updates either the ConfigMap or Secret, it performs the following

0 commit comments

Comments
 (0)