Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a property for configuring additional livereload paths #41566

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
*
* @author Phillip Webb
* @author Stephane Nicoll
* @author Akshay Dubey
* @since 1.3.0
*/
@ConfigurationProperties(prefix = "spring.devtools")
Expand Down Expand Up @@ -198,6 +199,22 @@ public static class Livereload {
*/
private int port = 35729;

/**
* Additional paths to watch for changes.
*/
private List<File> additionalPaths = new ArrayList<>();

/**
* Amount of time to wait between polling for classpath changes.
*/
private Duration pollInterval = Duration.ofSeconds(1);

/**
* Amount of quiet time required without any classpath changes before a reload is
* triggered.
*/
private Duration quietPeriod = Duration.ofMillis(400);

public boolean isEnabled() {
return this.enabled;
}
Expand All @@ -214,6 +231,30 @@ public void setPort(int port) {
this.port = port;
}

public List<File> getAdditionalPaths() {
return this.additionalPaths;
}

public void setAdditionalPaths(List<File> additionalPaths) {
this.additionalPaths = additionalPaths;
}

public Duration getPollInterval() {
return this.pollInterval;
}

public void setPollInterval(Duration pollInterval) {
this.pollInterval = pollInterval;
}

public Duration getQuietPeriod() {
return this.quietPeriod;
}

public void setQuietPeriod(Duration quietPeriod) {
this.quietPeriod = quietPeriod;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand Down Expand Up @@ -57,6 +59,7 @@
* @author Phillip Webb
* @author Andy Wilkinson
* @author Vladimir Tsanev
* @author Akshay Dubey
* @since 1.3.0
*/
@AutoConfiguration
Expand Down Expand Up @@ -89,6 +92,26 @@ LiveReloadServerEventListener liveReloadServerEventListener(OptionalLiveReloadSe
return new LiveReloadServerEventListener(liveReloadServer);
}

@Bean
LiveReloadForAdditionalPaths liveReloadForAdditionalPaths(LiveReloadServer liveReloadServer,
DevToolsProperties properties, FileSystemWatcher fileSystemWatcher) {
return new LiveReloadForAdditionalPaths(liveReloadServer, properties.getLivereload().getAdditionalPaths(),
fileSystemWatcher);
}

@Bean
@ConditionalOnMissingBean(RestartConfiguration.class)
FileSystemWatcher newFileSystemWatcher(DevToolsProperties properties) {
return new FileSystemWatcher(true, properties.getLivereload().getPollInterval(),
properties.getLivereload().getQuietPeriod());
}

@Bean
@ConditionalOnBean(RestartConfiguration.class)
FileSystemWatcher fileSystemWatcher(RestartConfiguration restartConfiguration) {
return restartConfiguration.newFileSystemWatcher();
}

}

/**
Expand Down Expand Up @@ -216,4 +239,30 @@ public void onApplicationEvent(ClassPathChangedEvent event) {

}

static class LiveReloadForAdditionalPaths implements DisposableBean {

private final FileSystemWatcher fileSystemWatcher;

@Override
public void destroy() throws Exception {
if (this.fileSystemWatcher != null) {
this.fileSystemWatcher.stop();
}
}

LiveReloadForAdditionalPaths(LiveReloadServer liveReloadServer, List<File> staticLocations,
FileSystemWatcher fileSystemWatcher) {
this.fileSystemWatcher = fileSystemWatcher;

for (File path : staticLocations) {
this.fileSystemWatcher.addSourceDirectory(path.getAbsoluteFile());
}

this.fileSystemWatcher.addListener((__) -> liveReloadServer.triggerReload());

this.fileSystemWatcher.start();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.WebProperties.Resources;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration.LiveReloadForAdditionalPaths;
import org.springframework.boot.devtools.classpath.ClassPathChangedEvent;
import org.springframework.boot.devtools.classpath.ClassPathFileSystemWatcher;
import org.springframework.boot.devtools.livereload.LiveReloadServer;
Expand Down Expand Up @@ -70,6 +71,7 @@
* @author Phillip Webb
* @author Andy Wilkinson
* @author Vladimir Tsanev
* @author Akshay Dubey
*/
@ExtendWith(MockRestarter.class)
class LocalDevToolsAutoConfigurationTests {
Expand Down Expand Up @@ -213,6 +215,21 @@ void watchingAdditionalPaths() throws Exception {
.containsKey(new File("src/test/java").getAbsoluteFile());
}

@Test
void watchingAdditionalPathsForReload() throws Exception {
Map<String, Object> properties = new HashMap<>();
properties.put("spring.devtools.livereload.additional-paths", "src/main/java,src/test/java");
this.context = getContext(() -> initializeAndRun(Config.class, properties));
LiveReloadForAdditionalPaths liveReloadForAdditionalPaths = this.context
.getBean(LiveReloadForAdditionalPaths.class);
Object watcher = ReflectionTestUtils.getField(liveReloadForAdditionalPaths, "fileSystemWatcher");
@SuppressWarnings("unchecked")
Map<File, Object> directories = (Map<File, Object>) ReflectionTestUtils.getField(watcher, "directories");
assertThat(directories).hasSize(2)
.containsKey(new File("src/main/java").getAbsoluteFile())
.containsKey(new File("src/test/java").getAbsoluteFile());
}

@Test
void devToolsSwitchesJspServletToDevelopmentMode() throws Exception {
this.context = getContext(() -> initializeAndRun(Config.class));
Expand Down