From bff93a67ceae383dcb087d98134002a7aaf85d89 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 19 Sep 2018 17:58:17 +0100 Subject: [PATCH] Fix remote DevTools restart when a previously added class is then changed Previously, if a class was added and then changed, a restart would be triggered and things would behave as if the class had been deleted. This occurred because, when looking for additional classes that were not on the original classpath, only files that had been added were considered. The subsequent change to the class was noticed as a modified rather than an addition, resulting in the class being skipped. This commit updates the resource resolver to only ignore deleted files and consider both added files and modified files when looking for additional resources. Closes gh-14205 --- ...assLoaderFilesResourcePatternResolver.java | 2 +- .../tests/DevToolsIntegrationTests.java | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java index 37d8d07809..3deed7db9f 100644 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/restart/ClassLoaderFilesResourcePatternResolver.java @@ -130,7 +130,7 @@ final class ClassLoaderFilesResourcePatternResolver implements ResourcePatternRe for (Entry entry : sourceFolder.getFilesEntrySet()) { String name = entry.getKey(); ClassLoaderFile file = entry.getValue(); - if (file.getKind() == Kind.ADDED + if (entry.getValue().getKind() != Kind.DELETED && this.antPathMatcher.match(trimmedLocationPattern, name)) { URL url = new URL("reloaded", null, -1, "/" + name, new ClassLoaderFileURLStreamHandler(file)); diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java index 9d3afa749c..9a141dd7e0 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/DevToolsIntegrationTests.java @@ -125,6 +125,53 @@ public class DevToolsIntegrationTests { } + @Test + public void createAControllerAndThenAddARequestMapping() throws Exception { + TestRestTemplate template = new TestRestTemplate(); + String urlBase = "http://localhost:" + awaitServerPort(); + assertThat(template.getForObject(urlBase + "/one", String.class)) + .isEqualTo("one"); + assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode()) + .isEqualTo(HttpStatus.NOT_FOUND); + controller("com.example.ControllerTwo").withRequestMapping("two").build(); + assertThat(template.getForObject(urlBase + "/one", String.class)) + .isEqualTo("one"); + assertThat(template.getForObject("http://localhost:" + awaitServerPort() + "/two", + String.class)).isEqualTo("two"); + controller("com.example.ControllerTwo").withRequestMapping("two") + .withRequestMapping("three").build(); + assertThat(template.getForObject( + "http://localhost:" + awaitServerPort() + "/three", String.class)) + .isEqualTo("three"); + } + + @Test + public void createAControllerAndThenAddARequestMappingToAnExistingController() + throws Exception { + TestRestTemplate template = new TestRestTemplate(); + String urlBase = "http://localhost:" + awaitServerPort(); + assertThat(template.getForObject(urlBase + "/one", String.class)) + .isEqualTo("one"); + assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode()) + .isEqualTo(HttpStatus.NOT_FOUND); + controller("com.example.ControllerTwo").withRequestMapping("two").build(); + assertThat(template.getForObject(urlBase + "/one", String.class)) + .isEqualTo("one"); + assertThat(template.getForObject("http://localhost:" + awaitServerPort() + "/two", + String.class)).isEqualTo("two"); + controller("com.example.ControllerOne").withRequestMapping("one") + .withRequestMapping("three").build(); + int port = awaitServerPort(); + assertThat( + template.getForObject("http://localhost:" + port + "/one", String.class)) + .isEqualTo("one"); + assertThat( + template.getForObject("http://localhost:" + port + "/two", String.class)) + .isEqualTo("two"); + assertThat(template.getForObject("http://localhost:" + port + "/three", + String.class)).isEqualTo("three"); + } + @Test public void deleteAController() throws Exception { TestRestTemplate template = new TestRestTemplate(); @@ -137,6 +184,25 @@ public class DevToolsIntegrationTests { } + @Test + public void createAControllerAndThenDeleteIt() throws Exception { + TestRestTemplate template = new TestRestTemplate(); + String urlBase = "http://localhost:" + awaitServerPort(); + assertThat(template.getForObject(urlBase + "/one", String.class)) + .isEqualTo("one"); + assertThat(template.getForEntity(urlBase + "/two", String.class).getStatusCode()) + .isEqualTo(HttpStatus.NOT_FOUND); + controller("com.example.ControllerTwo").withRequestMapping("two").build(); + assertThat(template.getForObject(urlBase + "/one", String.class)) + .isEqualTo("one"); + assertThat(template.getForObject("http://localhost:" + awaitServerPort() + "/two", + String.class)).isEqualTo("two"); + assertThat(new File(this.launchedApplication.getClassesDirectory(), + "com/example/ControllerTwo.class").delete()).isTrue(); + assertThat(template.getForEntity("http://localhost:" + awaitServerPort() + "/two", + String.class).getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND); + } + private int awaitServerPort() throws Exception { long end = System.currentTimeMillis() + 30000; while (this.serverPortFile.length() == 0) {