Allow custom restart pollInterval and quietPeriod

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

Fixes gh-3139
pull/3096/merge
Phillip Webb 10 years ago
parent 7bcd6567ba
commit d0349879c3

@ -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 {
*/ */
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 {
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;
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 {
@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;
*/ */
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 {
* 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 {
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 {
} }
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 {
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
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 {
@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 {
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 {
@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 {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SampleDevToolsApplication.class, args); SpringApplication.run(SampleDevToolsApplication.class, args);
} }
} }

@ -1,2 +1,3 @@
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

Loading…
Cancel
Save