Commit b47af199 authored by Dave Syer's avatar Dave Syer Committed by Phillip Webb

Stop file watcher as soon as a change is detected

The FileWatcher sometimes generates multiple events for a single change
and if there is a slow shutdown hook the second one can come in before
the context is closed, leaving it in a tricky state. This change
attempts to stop the file watcher as soon as it detects a change
(the stop() method is called in the listener, which normally happens in
the same thread as the scan).

Fixes gh-3097
parent 983484f4
......@@ -27,6 +27,7 @@ import org.springframework.boot.devtools.classpath.ClassPathChangedEvent;
import org.springframework.boot.devtools.classpath.ClassPathFileSystemWatcher;
import org.springframework.boot.devtools.classpath.ClassPathRestartStrategy;
import org.springframework.boot.devtools.classpath.PatternClassPathRestartStrategy;
import org.springframework.boot.devtools.filewatch.FileSystemWatcher;
import org.springframework.boot.devtools.livereload.LiveReloadServer;
import org.springframework.boot.devtools.restart.ConditionalOnInitializedRestarter;
import org.springframework.boot.devtools.restart.RestartScope;
......@@ -103,11 +104,14 @@ public class LocalDevToolsAutoConfiguration {
@Autowired
private DevToolsProperties properties;
private final FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
@Bean
@ConditionalOnMissingBean
public ClassPathFileSystemWatcher classPathFileSystemWatcher() {
URL[] urls = Restarter.getInstance().getInitialUrls();
return new ClassPathFileSystemWatcher(classPathRestartStrategy(), urls);
return new ClassPathFileSystemWatcher(this.fileSystemWatcher,
classPathRestartStrategy(), urls);
}
@Bean
......@@ -120,6 +124,7 @@ public class LocalDevToolsAutoConfiguration {
@EventListener
public void onClassPathChanged(ClassPathChangedEvent event) {
if (event.isRestartRequired()) {
this.fileSystemWatcher.stop();
Restarter.getInstance().restart();
}
}
......
......@@ -72,11 +72,11 @@ public class ClassPathFileSystemWatcher implements InitializingBean, DisposableB
* @param restartStrategy the classpath restart strategy
* @param urls the URLs to watch
*/
protected ClassPathFileSystemWatcher(FileSystemWatcher fileSystemWatcher,
public ClassPathFileSystemWatcher(FileSystemWatcher fileSystemWatcher,
ClassPathRestartStrategy restartStrategy, URL[] urls) {
Assert.notNull(fileSystemWatcher, "FileSystemWatcher must not be null");
Assert.notNull(urls, "Urls must not be null");
this.fileSystemWatcher = new FileSystemWatcher();
this.fileSystemWatcher = fileSystemWatcher;
this.restartStrategy = restartStrategy;
addUrls(urls);
}
......
......@@ -199,13 +199,16 @@ public class FileSystemWatcher {
Thread thread = this.watchThread;
if (thread != null) {
this.remainingScans.set(remainingScans);
try {
thread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
if (Thread.currentThread() != thread) {
try {
thread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
this.watchThread = null;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment