diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java index b8758a395a..b58c5abf90 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java @@ -328,7 +328,7 @@ class TomcatServletWebServerFactoryTests extends AbstractServletWebServerFactory } @Test - void startupFailureDoesNotResultInUnstoppedThreadsBeingReported(CapturedOutput output) throws IOException { + void startupFailureDoesNotResultInUnstoppedThreadsBeingReported(CapturedOutput output) throws Exception { super.portClashOfPrimaryConnectorResultsInPortInUseException(); assertThat(output).doesNotContain("appears to have started a thread named [main]"); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java index 339a4beef8..5efcd52c27 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java @@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.time.Duration; import java.util.Arrays; +import java.util.concurrent.Callable; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLException; @@ -87,12 +88,15 @@ public abstract class AbstractReactiveWebServerFactoryTests { protected abstract AbstractReactiveWebServerFactory getFactory(); @Test - void specificPort() { + void specificPort() throws Exception { AbstractReactiveWebServerFactory factory = getFactory(); - int specificPort = SocketUtils.findAvailableTcpPort(41000); - factory.setPort(specificPort); - this.webServer = factory.getWebServer(new EchoHandler()); - this.webServer.start(); + int specificPort = doWithRetry(() -> { + int port = SocketUtils.findAvailableTcpPort(41000); + factory.setPort(port); + this.webServer = factory.getWebServer(new EchoHandler()); + this.webServer.start(); + return port; + }); Mono result = getWebClient().build().post().uri("/test").contentType(MediaType.TEXT_PLAIN) .body(BodyInserters.fromValue("Hello World")).exchange() .flatMap((response) -> response.bodyToMono(String.class)); @@ -108,6 +112,7 @@ public abstract class AbstractReactiveWebServerFactoryTests { @Test void basicSslFromFileSystem() { testBasicSslWithKeyStore("src/test/resources/test.jks", "password"); + } protected final void testBasicSslWithKeyStore(String keyStore, String keyPassword) { @@ -329,6 +334,19 @@ public abstract class AbstractReactiveWebServerFactoryTests { assertThat(body).isEqualTo("https"); } + private T doWithRetry(Callable action) throws Exception { + Exception lastFailure = null; + for (int i = 0; i < 10; i++) { + try { + return action.call(); + } + catch (Exception ex) { + lastFailure = ex; + } + } + throw new IllegalStateException("Action was not successful in 10 attempts", lastFailure); + } + protected static class EchoHandler implements HttpHandler { public EchoHandler() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index 6a815adbca..bab8246d93 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -44,6 +44,7 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.GZIPInputStream; @@ -246,10 +247,13 @@ public abstract class AbstractServletWebServerFactoryTests { @Test void specificPort() throws Exception { AbstractServletWebServerFactory factory = getFactory(); - int specificPort = SocketUtils.findAvailableTcpPort(41000); - factory.setPort(specificPort); - this.webServer = factory.getWebServer(exampleServletRegistration()); - this.webServer.start(); + int specificPort = doWithRetry(() -> { + int port = SocketUtils.findAvailableTcpPort(41000); + factory.setPort(port); + this.webServer = factory.getWebServer(exampleServletRegistration()); + this.webServer.start(); + return port; + }); assertThat(getResponse("http://localhost:" + specificPort + "/hello")).isEqualTo("Hello World"); assertThat(this.webServer.getPort()).isEqualTo(specificPort); } @@ -823,7 +827,7 @@ public abstract class AbstractServletWebServerFactoryTests { } @Test - protected void portClashOfPrimaryConnectorResultsInPortInUseException() throws IOException { + protected void portClashOfPrimaryConnectorResultsInPortInUseException() throws Exception { doWithBlockedPort((port) -> { assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> { AbstractServletWebServerFactory factory = getFactory(); @@ -835,7 +839,7 @@ public abstract class AbstractServletWebServerFactoryTests { } @Test - void portClashOfSecondaryConnectorResultsInPortInUseException() throws IOException { + void portClashOfSecondaryConnectorResultsInPortInUseException() throws Exception { doWithBlockedPort((port) -> { assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> { AbstractServletWebServerFactory factory = getFactory(); @@ -1131,19 +1135,28 @@ public abstract class AbstractServletWebServerFactoryTests { return bean; } - protected final void doWithBlockedPort(BlockedPortAction action) throws IOException { - int port = SocketUtils.findAvailableTcpPort(40000); - ServerSocket serverSocket = new ServerSocket(); + private T doWithRetry(Callable action) throws Exception { + Exception lastFailure = null; for (int i = 0; i < 10; i++) { try { - serverSocket.bind(new InetSocketAddress(port)); - break; + return action.call(); } catch (Exception ex) { + lastFailure = ex; } } + throw new IllegalStateException("Action was not successful in 10 attempts", lastFailure); + } + + protected final void doWithBlockedPort(BlockedPortAction action) throws Exception { + ServerSocket serverSocket = new ServerSocket(); + int blockedPort = doWithRetry(() -> { + int port = SocketUtils.findAvailableTcpPort(40000); + serverSocket.bind(new InetSocketAddress(port)); + return port; + }); try { - action.run(port); + action.run(blockedPort); } finally { serverSocket.close();