Commit d0349879 authored by Phillip Webb's avatar Phillip Webb

Allow custom restart pollInterval and quietPeriod

Allow the pollInterval and the quietPeriod of the filewatcher to be
configured.

Fixes gh-3139
parent 7bcd6567
...@@ -29,6 +29,10 @@ public class DevToolsProperties { ...@@ -29,6 +29,10 @@ public class DevToolsProperties {
private static final String DEFAULT_RESTART_EXCLUDES = "META-INF/resources/**,resource/**,static/**,public/**,templates/**"; private static final String DEFAULT_RESTART_EXCLUDES = "META-INF/resources/**,resource/**,static/**,public/**,templates/**";
private static final long DEFAULT_RESTART_POLL_INTERVAL = 1000;
private static final long DEFAULT_RESTART_QUIET_PERIOD = 400;
private Restart restart = new Restart(); private Restart restart = new Restart();
private Livereload livereload = new Livereload(); private Livereload livereload = new Livereload();
...@@ -62,6 +66,17 @@ public class DevToolsProperties { ...@@ -62,6 +66,17 @@ public class DevToolsProperties {
*/ */
private String exclude = DEFAULT_RESTART_EXCLUDES; private String exclude = DEFAULT_RESTART_EXCLUDES;
/**
* Amount of time (in milliseconds) to wait between polling for classpath changes.
*/
private long pollInterval = DEFAULT_RESTART_POLL_INTERVAL;
/**
* Amount of quiet time (in milliseconds) requited without any classpath changes
* before a restart is triggered.
*/
private long quietPeriod = DEFAULT_RESTART_QUIET_PERIOD;
/** /**
* The name of specific that that when changed will will trigger the restart. If * The name of specific that that when changed will will trigger the restart. If
* not specified any classpath file change will trigger the restart. * not specified any classpath file change will trigger the restart.
...@@ -84,6 +99,22 @@ public class DevToolsProperties { ...@@ -84,6 +99,22 @@ public class DevToolsProperties {
this.exclude = exclude; this.exclude = exclude;
} }
public long getPollInterval() {
return this.pollInterval;
}
public void setPollInterval(long pollInterval) {
this.pollInterval = pollInterval;
}
public long getQuietPeriod() {
return this.quietPeriod;
}
public void setQuietPeriod(long quietPeriod) {
this.quietPeriod = quietPeriod;
}
public String getTriggerFile() { public String getTriggerFile() {
return this.triggerFile; return this.triggerFile;
} }
......
...@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; ...@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.devtools.autoconfigure.DevToolsProperties.Restart;
import org.springframework.boot.devtools.classpath.ClassPathChangedEvent; import org.springframework.boot.devtools.classpath.ClassPathChangedEvent;
import org.springframework.boot.devtools.classpath.ClassPathFileSystemWatcher; import org.springframework.boot.devtools.classpath.ClassPathFileSystemWatcher;
import org.springframework.boot.devtools.classpath.ClassPathRestartStrategy; import org.springframework.boot.devtools.classpath.ClassPathRestartStrategy;
...@@ -130,8 +131,11 @@ public class LocalDevToolsAutoConfiguration { ...@@ -130,8 +131,11 @@ public class LocalDevToolsAutoConfiguration {
@Bean @Bean
public FileSystemWatcher getFileSystemWatcher() { public FileSystemWatcher getFileSystemWatcher() {
FileSystemWatcher watcher = new FileSystemWatcher(); Restart restartProperties = this.properties.getRestart();
String triggerFile = this.properties.getRestart().getTriggerFile(); FileSystemWatcher watcher = new FileSystemWatcher(true,
restartProperties.getPollInterval(),
restartProperties.getQuietPeriod());
String triggerFile = restartProperties.getTriggerFile();
if (StringUtils.hasLength(triggerFile)) { if (StringUtils.hasLength(triggerFile)) {
watcher.setTriggerFilter(new TriggerFileFilter(triggerFile)); watcher.setTriggerFilter(new TriggerFileFilter(triggerFile));
} }
......
...@@ -40,17 +40,17 @@ import org.springframework.util.Assert; ...@@ -40,17 +40,17 @@ import org.springframework.util.Assert;
*/ */
public class FileSystemWatcher { public class FileSystemWatcher {
private static final long DEFAULT_IDLE_TIME = 400; private static final long DEFAULT_POLL_INTERVAL = 1000;
private static final long DEFAULT_QUIET_TIME = 200; private static final long DEFAULT_QUIET_PERIOD = 400;
private List<FileChangeListener> listeners = new ArrayList<FileChangeListener>(); private List<FileChangeListener> listeners = new ArrayList<FileChangeListener>();
private final boolean daemon; private final boolean daemon;
private final long idleTime; private final long pollInterval;
private final long quietTime; private final long quietPeriod;
private Thread watchThread; private Thread watchThread;
...@@ -64,20 +64,24 @@ public class FileSystemWatcher { ...@@ -64,20 +64,24 @@ public class FileSystemWatcher {
* Create a new {@link FileSystemWatcher} instance. * Create a new {@link FileSystemWatcher} instance.
*/ */
public FileSystemWatcher() { public FileSystemWatcher() {
this(true, DEFAULT_IDLE_TIME, DEFAULT_QUIET_TIME); this(true, DEFAULT_POLL_INTERVAL, DEFAULT_QUIET_PERIOD);
} }
/** /**
* Create a new {@link FileSystemWatcher} instance. * Create a new {@link FileSystemWatcher} instance.
* @param daemon if a daemon thread used to monitor changes * @param daemon if a daemon thread used to monitor changes
* @param idleTime the amount of time to wait between checking for changes * @param pollInterval the amount of time to wait between checking for changes
* @param quietTime the amount of time required after a change has been detected to * @param quietPeriod the amount of time required after a change has been detected to
* ensure that updates have completed * ensure that updates have completed
*/ */
public FileSystemWatcher(boolean daemon, long idleTime, long quietTime) { public FileSystemWatcher(boolean daemon, long pollInterval, long quietPeriod) {
Assert.isTrue(pollInterval > 0, "PollInterval must be positive");
Assert.isTrue(quietPeriod > 0, "QuietPeriod must be positive");
Assert.isTrue(pollInterval > quietPeriod,
"PollInterval must be greater than QuietPeriod");
this.daemon = daemon; this.daemon = daemon;
this.idleTime = idleTime; this.pollInterval = pollInterval;
this.quietTime = quietTime; this.quietPeriod = quietPeriod;
} }
/** /**
...@@ -131,10 +135,10 @@ public class FileSystemWatcher { ...@@ -131,10 +135,10 @@ public class FileSystemWatcher {
FileSystemWatcher.this.remainingScans.decrementAndGet(); FileSystemWatcher.this.remainingScans.decrementAndGet();
} }
scan(); scan();
remainingScans = FileSystemWatcher.this.remainingScans.get();
} }
catch (InterruptedException ex) { catch (InterruptedException ex) {
} }
remainingScans = FileSystemWatcher.this.remainingScans.get();
} }
}; };
}; };
...@@ -152,13 +156,13 @@ public class FileSystemWatcher { ...@@ -152,13 +156,13 @@ public class FileSystemWatcher {
} }
private void scan() throws InterruptedException { private void scan() throws InterruptedException {
Thread.sleep(this.idleTime - this.quietTime); Thread.sleep(this.pollInterval - this.quietPeriod);
Map<File, FolderSnapshot> previous; Map<File, FolderSnapshot> previous;
Map<File, FolderSnapshot> current = this.folders; Map<File, FolderSnapshot> current = this.folders;
do { do {
previous = current; previous = current;
current = getCurrentSnapshots(); current = getCurrentSnapshots();
Thread.sleep(this.quietTime); Thread.sleep(this.quietPeriod);
} }
while (isDifferent(previous, current)); while (isDifferent(previous, current));
if (isDifferent(this.folders, current)) { if (isDifferent(this.folders, current)) {
...@@ -228,6 +232,7 @@ public class FileSystemWatcher { ...@@ -228,6 +232,7 @@ public class FileSystemWatcher {
Thread thread = this.watchThread; Thread thread = this.watchThread;
if (thread != null) { if (thread != null) {
this.remainingScans.set(remainingScans); this.remainingScans.set(remainingScans);
thread.interrupt();
if (Thread.currentThread() != thread) { if (Thread.currentThread() != thread) {
try { try {
thread.join(); thread.join();
......
...@@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean ...@@ -34,6 +34,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.devtools.autoconfigure.DevToolsProperties; import org.springframework.boot.devtools.autoconfigure.DevToolsProperties;
import org.springframework.boot.devtools.autoconfigure.DevToolsProperties.Restart;
import org.springframework.boot.devtools.autoconfigure.OptionalLiveReloadServer; import org.springframework.boot.devtools.autoconfigure.OptionalLiveReloadServer;
import org.springframework.boot.devtools.autoconfigure.RemoteDevToolsProperties; import org.springframework.boot.devtools.autoconfigure.RemoteDevToolsProperties;
import org.springframework.boot.devtools.autoconfigure.TriggerFileFilter; import org.springframework.boot.devtools.autoconfigure.TriggerFileFilter;
...@@ -187,8 +188,11 @@ public class RemoteClientConfiguration { ...@@ -187,8 +188,11 @@ public class RemoteClientConfiguration {
@Bean @Bean
public FileSystemWatcher getFileSystemWather() { public FileSystemWatcher getFileSystemWather() {
FileSystemWatcher watcher = new FileSystemWatcher(); Restart restartProperties = this.properties.getRestart();
String triggerFile = this.properties.getRestart().getTriggerFile(); FileSystemWatcher watcher = new FileSystemWatcher(true,
restartProperties.getPollInterval(),
restartProperties.getQuietPeriod());
String triggerFile = restartProperties.getTriggerFile();
if (StringUtils.hasLength(triggerFile)) { if (StringUtils.hasLength(triggerFile)) {
watcher.setTriggerFilter(new TriggerFileFilter(triggerFile)); watcher.setTriggerFilter(new TriggerFileFilter(triggerFile));
} }
......
...@@ -64,6 +64,27 @@ public class FileSystemWatcherTests { ...@@ -64,6 +64,27 @@ public class FileSystemWatcherTests {
setupWatcher(20, 10); setupWatcher(20, 10);
} }
@Test
public void pollIntervalMustBePositive() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("PollInterval must be positive");
new FileSystemWatcher(true, 0, 1);
}
@Test
public void quietPeriodMustBePositive() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("QuietPeriod must be positive");
new FileSystemWatcher(true, 1, 0);
}
@Test
public void pollIntervalMustBeGreaterThanQuietPeriod() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("PollInterval must be greater than QuietPeriod");
new FileSystemWatcher(true, 1, 1);
}
@Test @Test
public void listenerMustNotBeNull() throws Exception { public void listenerMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
...@@ -117,7 +138,7 @@ public class FileSystemWatcherTests { ...@@ -117,7 +138,7 @@ public class FileSystemWatcherTests {
@Test @Test
public void waitsForIdleTime() throws Exception { public void waitsForIdleTime() throws Exception {
this.changes.clear(); this.changes.clear();
setupWatcher(100, 0); setupWatcher(100, 1);
File folder = startWithNewFolder(); File folder = startWithNewFolder();
touch(new File(folder, "test1.txt")); touch(new File(folder, "test1.txt"));
Thread.sleep(200); Thread.sleep(200);
......
...@@ -26,5 +26,4 @@ public class SampleDevToolsApplication extends WebMvcAutoConfigurationAdapter { ...@@ -26,5 +26,4 @@ public class SampleDevToolsApplication extends WebMvcAutoConfigurationAdapter {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SampleDevToolsApplication.class, args); SpringApplication.run(SampleDevToolsApplication.class, args);
} }
} }
spring.devtools.remote.secret=secret spring.devtools.remote.secret=secret
# spring.devtools.restart.poll-interval=10000
# spring.devtools.restart.trigger-file=.reloadtrigger # spring.devtools.restart.trigger-file=.reloadtrigger
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