From f7e9ec5f42a20f971b6ec8c7f7b681290377d376 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 8 Jun 2017 10:37:40 +0100 Subject: [PATCH] Minimise our usage of SocketUtils.findAvailableTcpPort Closes gh-9382 --- .../EndpointWebMvcAutoConfigurationTests.java | 108 ++++++------------ .../PublicMetricsAutoConfigurationTests.java | 5 +- .../endpoint/TomcatPublicMetricsTests.java | 5 +- .../statsd/StatsdMetricWriterTests.java | 15 +-- .../jest/JestAutoConfigurationTests.java | 69 ++++++++++- .../EmbeddedMongoAutoConfigurationTests.java | 5 +- .../boot/cli/WarCommandIT.java | 7 +- .../springframework/boot/cli/CliTester.java | 35 ++++-- .../boot/cli/CliTesterSpringApplication.java | 39 +++++++ .../devtools/livereload/LiveReloadServer.java | 5 +- .../devtools/tunnel/client/TunnelClient.java | 15 ++- .../LocalDevToolsAutoConfigurationTests.java | 5 +- .../HttpTunnelIntegrationTests.java | 91 +++++++++------ .../livereload/LiveReloadServerTests.java | 7 +- ...LocalDebugPortAvailableConditionTests.java | 17 ++- .../RemoteClientConfigurationTests.java | 53 ++++++--- .../client/HttpTunnelConnectionTests.java | 10 +- .../tunnel/client/TunnelClientTests.java | 38 +++--- .../SocketTargetServerConnectionTests.java | 20 ++-- .../tests/RemoteApplicationLauncher.java | 46 +++++++- .../SampleTomcatTwoConnectorsApplication.java | 8 +- ...leTomcatTwoConnectorsApplicationTests.java | 61 +++++++--- ...omContainerWebSocketsApplicationTests.java | 15 +-- ...omContainerWebSocketsApplicationTests.java | 15 +-- ...omContainerWebSocketsApplicationTests.java | 15 +-- .../TomcatServletWebServerFactoryTests.java | 3 +- 26 files changed, 434 insertions(+), 278 deletions(-) create mode 100644 spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTesterSpringApplication.java diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java index 6d095de08f..44121dfb2e 100755 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfigurationTests.java @@ -17,8 +17,6 @@ package org.springframework.boot.actuate.autoconfigure; import java.io.FileNotFoundException; -import java.net.InetSocketAddress; -import java.net.ServerSocket; import java.net.SocketException; import java.net.URI; import java.nio.charset.Charset; @@ -67,12 +65,10 @@ import org.springframework.boot.logging.LoggingSystem; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.testutil.Matched; import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer; +import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; -import org.springframework.boot.web.server.WebServer; -import org.springframework.boot.web.server.WebServerException; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; -import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationListener; @@ -80,6 +76,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpRequest; @@ -88,7 +85,6 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Controller; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.SocketUtils; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @@ -134,7 +130,6 @@ public class EndpointWebMvcAutoConfigurationTests { @After public void cleanUp() throws Exception { this.applicationContext.close(); - assertAllClosed(); } @Test @@ -146,8 +141,6 @@ public class EndpointWebMvcAutoConfigurationTests { this.applicationContext.refresh(); assertContent("/controller", ports.get().server, "controlleroutput"); assertContent("/endpoint", ports.get().server, "endpointoutput"); - assertContent("/controller", ports.get().management, null); - assertContent("/endpoint", ports.get().management, null); assertThat(hasHeader("/endpoint", ports.get().server, "X-Application-Context")) .isFalse(); assertThat(this.applicationContext.containsBean("applicationContextIdFilter")) @@ -274,25 +267,6 @@ public class EndpointWebMvcAutoConfigurationTests { assertContent("/endpoint", ports.get().management, null); } - @Test - public void onRandomPort() throws Exception { - TestPropertyValues.of("management.port=0", "management.security.enabled=false") - .applyTo(this.applicationContext); - this.applicationContext.register(RootConfig.class, EndpointConfig.class, - BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class, - ErrorMvcAutoConfiguration.class); - GrabManagementPort grabManagementPort = new GrabManagementPort( - this.applicationContext); - this.applicationContext.addApplicationListener(grabManagementPort); - this.applicationContext.refresh(); - int managementPort = grabManagementPort.getWebServer().getPort(); - assertThat(managementPort).isNotEqualTo(ports.get().server); - assertContent("/controller", ports.get().server, "controlleroutput"); - assertContent("/endpoint", ports.get().server, null); - assertContent("/controller", managementPort, null); - assertContent("/endpoint", managementPort, "endpointoutput"); - } - @Test public void onDifferentPortWithPrimaryFailure() throws Exception { TestPropertyValues.of("management.port=" + ports.get().management) @@ -340,20 +314,22 @@ public class EndpointWebMvcAutoConfigurationTests { } @Test - public void specificPortsViaPropertiesWithClash() throws Exception { - int managementPort = ports.get().management; - try (ServerSocket serverSocket = new ServerSocket()) { - serverSocket.bind(new InetSocketAddress(managementPort)); - TestPropertyValues - .of("server.port:" + ports.get().server, - "management.port:" + ports.get().management) - .applyTo(this.applicationContext); - this.applicationContext.register(RootConfig.class, EndpointConfig.class, - BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class, - ErrorMvcAutoConfiguration.class); - this.thrown.expect(WebServerException.class); - this.applicationContext.refresh(); - } + public void managementContextFailureCausesMainContextFailure() throws Exception { + TestPropertyValues + .of("server.port:" + ports.get().server, + "management.port:" + ports.get().management) + .applyTo(this.applicationContext); + this.applicationContext.register(RootConfig.class, EndpointConfig.class, + BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class, + ErrorMvcAutoConfiguration.class); + this.applicationContext.addApplicationListener( + (ApplicationListener) (event) -> { + if (event.getApplicationContext().getParent() != null) { + throw new RuntimeException(); + } + }); + this.thrown.expect(RuntimeException.class); + this.applicationContext.refresh(); } @Test @@ -671,13 +647,6 @@ public class EndpointWebMvcAutoConfigurationTests { assertThat(this.applicationContext.getBeansOfType(type)).hasSize(1); } - private void assertAllClosed() throws Exception { - assertContent("/controller", ports.get().server, null); - assertContent("/endpoint", ports.get().server, null); - assertContent("/controller", ports.get().management, null); - assertContent("/endpoint", ports.get().management, null); - } - private void assertHttpsContent(String url, int port, Object expected) throws Exception { assertContent("https", url, port, expected); @@ -733,9 +702,9 @@ public class EndpointWebMvcAutoConfigurationTests { private static class Ports { - final int server = SocketUtils.findAvailableTcpPort(); + int server = 0; - final int management = SocketUtils.findAvailableTcpPort(); + int management = 0; } @@ -758,6 +727,19 @@ public class EndpointWebMvcAutoConfigurationTests { return new TestController(); } + @Bean + public ApplicationListener serverPortListener() { + return (event) -> { + int port = event.getWebServer().getPort(); + if (event.getApplicationContext().getParent() == null) { + ports.get().server = port; + } + else { + ports.get().management = port; + } + }; + } + } @Configuration @@ -886,30 +868,6 @@ public class EndpointWebMvcAutoConfigurationTests { } - private static class GrabManagementPort - implements ApplicationListener { - - private ApplicationContext rootContext; - - private WebServer webServer; - - GrabManagementPort(ApplicationContext rootContext) { - this.rootContext = rootContext; - } - - @Override - public void onApplicationEvent(ServletWebServerInitializedEvent event) { - if (event.getApplicationContext() != this.rootContext) { - this.webServer = event.getWebServer(); - } - } - - public WebServer getWebServer() { - return this.webServer; - } - - } - private static class SpecificServletWebServerFactory extends TomcatServletWebServerFactory { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java index a0c7eec552..dcc79d6487 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfigurationTests.java @@ -57,7 +57,6 @@ import org.springframework.core.annotation.Order; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -347,9 +346,7 @@ public class PublicMetricsAutoConfigurationTests { @Bean public TomcatServletWebServerFactory webServerFactory() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - factory.setPort(SocketUtils.findAvailableTcpPort(40000)); - return factory; + return new TomcatServletWebServerFactory(0); } } diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetricsTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetricsTests.java index 3a073929fc..9e0f31ee80 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetricsTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/TomcatPublicMetricsTests.java @@ -25,7 +25,6 @@ import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactor import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -55,9 +54,7 @@ public class TomcatPublicMetricsTests { @Bean public TomcatServletWebServerFactory webServerFactory() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - factory.setPort(SocketUtils.findAvailableTcpPort(40000)); - return factory; + return new TomcatServletWebServerFactory(0); } @Bean diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java index 92ac5e265b..4bd5295825 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/metrics/statsd/StatsdMetricWriterTests.java @@ -28,7 +28,6 @@ import org.junit.Test; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.writer.Delta; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -40,12 +39,10 @@ import static org.assertj.core.api.Assertions.assertThat; */ public class StatsdMetricWriterTests { - private int port = SocketUtils.findAvailableTcpPort(); - - private DummyStatsDServer server = new DummyStatsDServer(this.port); + private DummyStatsDServer server = new DummyStatsDServer(0); private StatsdMetricWriter writer = new StatsdMetricWriter("me", "localhost", - this.port); + this.server.getPort()); @After public void close() { @@ -84,7 +81,7 @@ public class StatsdMetricWriterTests { @Test public void nullPrefix() throws Exception { - this.writer = new StatsdMetricWriter("localhost", this.port); + this.writer = new StatsdMetricWriter("localhost", this.server.getPort()); this.writer.set(new Metric<>("gauge.foo", 3L)); this.server.waitForMessage(); assertThat(this.server.messagesReceived().get(0)).isEqualTo("gauge.foo:3|g"); @@ -92,7 +89,7 @@ public class StatsdMetricWriterTests { @Test public void periodPrefix() throws Exception { - this.writer = new StatsdMetricWriter("my.", "localhost", this.port); + this.writer = new StatsdMetricWriter("my.", "localhost", this.server.getPort()); this.writer.set(new Metric<>("gauge.foo", 3L)); this.server.waitForMessage(); assertThat(this.server.messagesReceived().get(0)).isEqualTo("my.gauge.foo:3|g"); @@ -129,6 +126,10 @@ public class StatsdMetricWriterTests { new Thread(this).start(); } + int getPort() { + return this.server.getLocalPort(); + } + public void stop() { this.server.close(); } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/jest/JestAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/jest/JestAutoConfigurationTests.java index a4333d6e2d..8e8660460c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/jest/JestAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/elasticsearch/jest/JestAutoConfigurationTests.java @@ -16,7 +16,11 @@ package org.springframework.boot.autoconfigure.elasticsearch.jest; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -26,6 +30,7 @@ import io.searchbox.client.config.HttpClientConfig; import io.searchbox.client.http.JestHttpClient; import io.searchbox.core.Index; import io.searchbox.core.Search; +import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.junit.After; @@ -33,15 +38,20 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.beans.BeansException; +import org.springframework.beans.FatalBeanException; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -105,12 +115,12 @@ public class JestAutoConfigurationTests { @Test public void jestCanCommunicateWithElasticsearchInstance() throws IOException { - int port = SocketUtils.findAvailableTcpPort(); - load(ElasticsearchAutoConfiguration.class, + new File("target/elastic/logs").mkdirs(); + load(HttpPortConfiguration.class, "spring.data.elasticsearch.properties.path.home:target/elastic", "spring.data.elasticsearch.properties.http.enabled:true", - "spring.data.elasticsearch.properties.http.port:" + port, - "spring.elasticsearch.jest.uris:http://localhost:" + port); + "spring.data.elasticsearch.properties.http.port:0", + "spring.data.elasticsearch.properties.node.portsfile:true"); JestClient client = this.context.getBean(JestClient.class); Map source = new HashMap<>(); source.put("a", "alpha"); @@ -182,4 +192,53 @@ public class JestAutoConfigurationTests { } + @Configuration + @Import(ElasticsearchAutoConfiguration.class) + static class HttpPortConfiguration { + + @Bean + public static BeanPostProcessor portPropertyConfigurer() { + return new PortPropertyConfigurer(); + } + + private static final class PortPropertyConfigurer + implements BeanPostProcessor, ApplicationContextAware { + + private ConfigurableApplicationContext applicationContext; + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + if (bean instanceof NodeClient) { + this.applicationContext.getBean(JestProperties.class) + .setUris(Arrays.asList("http://localhost:" + readHttpPort())); + } + return bean; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = (ConfigurableApplicationContext) applicationContext; + } + + private int readHttpPort() { + try { + for (String line : Files + .readAllLines(Paths.get("target/elastic/logs/http.ports"))) { + if (line.startsWith("127.0.0.1")) { + return Integer + .parseInt(line.substring(line.indexOf(":") + 1)); + } + } + throw new FatalBeanException("HTTP port not found"); + } + catch (IOException ex) { + throw new FatalBeanException("Failed to read HTTP port", ex); + } + } + } + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfigurationTests.java index 810d44e663..a7265f15b0 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/embedded/EmbeddedMongoAutoConfigurationTests.java @@ -38,7 +38,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.util.FileSystemUtils; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -157,9 +156,7 @@ public class EmbeddedMongoAutoConfigurationTests { private void assertVersionConfiguration(String configuredVersion, String expectedVersion) { this.context = new AnnotationConfigApplicationContext(); - int mongoPort = SocketUtils.findAvailableTcpPort(); - TestPropertyValues.of("spring.data.mongodb.port=" + mongoPort) - .applyTo(this.context); + TestPropertyValues.of("spring.data.mongodb.port=0").applyTo(this.context); if (configuredVersion != null) { TestPropertyValues.of("spring.mongodb.embedded.version=" + configuredVersion) .applyTo(this.context); diff --git a/spring-boot-cli/src/it/java/org/springframework/boot/cli/WarCommandIT.java b/spring-boot-cli/src/it/java/org/springframework/boot/cli/WarCommandIT.java index 150d24751d..19e23836ec 100644 --- a/spring-boot-cli/src/it/java/org/springframework/boot/cli/WarCommandIT.java +++ b/spring-boot-cli/src/it/java/org/springframework/boot/cli/WarCommandIT.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import org.springframework.boot.cli.command.archive.WarCommand; import org.springframework.boot.cli.infrastructure.CommandLineInvoker; import org.springframework.boot.cli.infrastructure.CommandLineInvoker.Invocation; import org.springframework.boot.loader.tools.JavaExecutable; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -41,15 +40,13 @@ public class WarCommandIT { @Test public void warCreation() throws Exception { - int port = SocketUtils.findAvailableTcpPort(); File war = new File("target/test-app.war"); Invocation invocation = this.cli.invoke("war", war.getAbsolutePath(), "war.groovy"); invocation.await(); assertThat(war.exists()).isTrue(); Process process = new JavaExecutable() - .processBuilder("-jar", war.getAbsolutePath(), "--server.port=" + port) - .start(); + .processBuilder("-jar", war.getAbsolutePath(), "--server.port=0").start(); invocation = new Invocation(process); invocation.await(); assertThat(invocation.getOutput()).contains("onStart error"); diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java index 08f3bc96c6..f25765d8b7 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTester.java @@ -18,6 +18,7 @@ package org.springframework.boot.cli; import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; @@ -41,7 +42,7 @@ import org.springframework.boot.cli.command.archive.JarCommand; import org.springframework.boot.cli.command.grab.GrabCommand; import org.springframework.boot.cli.command.run.RunCommand; import org.springframework.boot.test.rule.OutputCapture; -import org.springframework.util.SocketUtils; +import org.springframework.util.FileCopyUtils; /** * {@link TestRule} that can be used to invoke CLI commands. @@ -60,8 +61,6 @@ public class CliTester implements TestRule { private final String prefix; - private final int port = SocketUtils.findAvailableTcpPort(); - public CliTester(String prefix) { this.prefix = prefix; } @@ -71,7 +70,21 @@ public class CliTester implements TestRule { } public String run(String... args) throws Exception { - Future future = submitCommand(new RunCommand(), args); + List updatedArgs = new ArrayList<>(); + boolean classpathUpdated = false; + for (String arg : args) { + if (arg.startsWith("--classpath=")) { + arg = arg + ":" + new File("target/test-classes").getAbsolutePath(); + classpathUpdated = true; + } + updatedArgs.add(arg); + } + if (!classpathUpdated) { + updatedArgs.add( + "--classpath=.:" + new File("target/test-classes").getAbsolutePath()); + } + Future future = submitCommand(new RunCommand(), + updatedArgs.toArray(new String[updatedArgs.size()])); this.commands.add(future.get(this.timeout, TimeUnit.MILLISECONDS)); return getOutput(); } @@ -96,13 +109,19 @@ public class CliTester implements TestRule { @Override public T call() throws Exception { ClassLoader loader = Thread.currentThread().getContextClassLoader(); - System.setProperty("server.port", String.valueOf(CliTester.this.port)); + System.setProperty("server.port", "0"); + System.setProperty("spring.application.class.name", + "org.springframework.boot.cli.CliTesterSpringApplication"); + System.setProperty("portfile", + new File("target/server.port").getAbsolutePath()); try { command.run(sources); return command; } finally { System.clearProperty("server.port"); + System.clearProperty("spring.application.class.name"); + System.clearProperty("portfile"); Thread.currentThread().setContextClassLoader(loader); } } @@ -137,7 +156,7 @@ public class CliTester implements TestRule { } } else { - sources[i] = this.prefix + arg; + sources[i] = new File(arg).isAbsolute() ? arg : this.prefix + arg; } } return sources; @@ -172,7 +191,9 @@ public class CliTester implements TestRule { public String getHttpOutput(String uri) { try { - InputStream stream = URI.create("http://localhost:" + this.port + uri).toURL() + int port = Integer.parseInt( + FileCopyUtils.copyToString(new FileReader("target/server.port"))); + InputStream stream = URI.create("http://localhost:" + port + uri).toURL() .openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); String line; diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTesterSpringApplication.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTesterSpringApplication.java new file mode 100644 index 0000000000..dcf024da9f --- /dev/null +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/CliTesterSpringApplication.java @@ -0,0 +1,39 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.cli; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.system.EmbeddedServerPortFileWriter; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * Custom {@link SpringApplication} used by {@link CliTester}. + * + * @author Andy Wilkinson + */ +public class CliTesterSpringApplication extends SpringApplication { + + public CliTesterSpringApplication(Class... sources) { + super(sources); + } + + @Override + protected void postProcessApplicationContext(ConfigurableApplicationContext context) { + context.addApplicationListener(new EmbeddedServerPortFileWriter()); + } + +} diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java index 9a5e81449f..d14fc81aad 100644 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/livereload/LiveReloadServer.java @@ -112,13 +112,15 @@ public class LiveReloadServer { /** * Start the livereload server and accept incoming connections. + * @return the port on which the server is listening * @throws IOException in case of I/O errors */ - public void start() throws IOException { + public int start() throws IOException { synchronized (this.monitor) { Assert.state(!isStarted(), "Server already started"); logger.debug("Starting live reload server on port " + this.port); this.serverSocket = new ServerSocket(this.port); + int localPort = this.serverSocket.getLocalPort(); this.listenThread = this.threadFactory.newThread(new Runnable() { @Override @@ -130,6 +132,7 @@ public class LiveReloadServer { this.listenThread.setDaemon(true); this.listenThread.setName("Live Reload Server"); this.listenThread.start(); + return localPort; } } diff --git a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/tunnel/client/TunnelClient.java b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/tunnel/client/TunnelClient.java index 00edb905e4..2289af9c54 100644 --- a/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/tunnel/client/TunnelClient.java +++ b/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/tunnel/client/TunnelClient.java @@ -57,7 +57,7 @@ public class TunnelClient implements SmartInitializingSingleton { private ServerThread serverThread; public TunnelClient(int listenPort, TunnelConnection tunnelConnection) { - Assert.isTrue(listenPort > 0, "ListenPort must be positive"); + Assert.isTrue(listenPort >= 0, "ListenPort must be greater than or equal to 0"); Assert.notNull(tunnelConnection, "TunnelConnection must not be null"); this.listenPort = listenPort; this.tunnelConnection = tunnelConnection; @@ -78,18 +78,20 @@ public class TunnelClient implements SmartInitializingSingleton { } /** - * Start the client and accept incoming connections on the port. + * Start the client and accept incoming connections. + * @return the port on which the client is listening * @throws IOException in case of I/O errors */ - public void start() throws IOException { + public int start() throws IOException { synchronized (this.monitor) { Assert.state(this.serverThread == null, "Server already started"); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(this.listenPort)); - logger.trace( - "Listening for TCP traffic to tunnel on port " + this.listenPort); + int port = serverSocketChannel.socket().getLocalPort(); + logger.trace("Listening for TCP traffic to tunnel on port " + port); this.serverThread = new ServerThread(serverSocketChannel); this.serverThread.start(); + return port; } } @@ -100,7 +102,6 @@ public class TunnelClient implements SmartInitializingSingleton { public void stop() throws IOException { synchronized (this.monitor) { if (this.serverThread != null) { - logger.trace("Closing tunnel client on port " + this.listenPort); this.serverThread.close(); try { this.serverThread.join(2000); @@ -143,6 +144,8 @@ public class TunnelClient implements SmartInitializingSingleton { } public void close() throws IOException { + logger.trace("Closing tunnel client on port " + + this.serverSocketChannel.socket().getLocalPort()); this.serverSocketChannel.close(); this.acceptConnections = false; interrupt(); diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfigurationTests.java index 1dd0bfd9ad..fbd876d53e 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfigurationTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/LocalDevToolsAutoConfigurationTests.java @@ -53,7 +53,6 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -77,8 +76,6 @@ public class LocalDevToolsAutoConfigurationTests { @Rule public MockRestarter mockRestarter = new MockRestarter(); - private int liveReloadPort = SocketUtils.findAvailableTcpPort(); - private ConfigurableApplicationContext context; @After @@ -270,7 +267,7 @@ public class LocalDevToolsAutoConfigurationTests { Map specifiedProperties) { Map properties = new HashMap<>(); properties.put("spring.thymeleaf.check-template-location", false); - properties.put("spring.devtools.livereload.port", this.liveReloadPort); + properties.put("spring.devtools.livereload.port", 0); properties.put("server.port", 0); properties.putAll(specifiedProperties); return properties; diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/integrationtest/HttpTunnelIntegrationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/integrationtest/HttpTunnelIntegrationTests.java index 9fa46465d0..5868b9dc48 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/integrationtest/HttpTunnelIntegrationTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/integrationtest/HttpTunnelIntegrationTests.java @@ -16,13 +16,14 @@ package org.springframework.boot.devtools.integrationtest; +import java.io.IOException; import java.util.Collection; import java.util.Collections; import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.devtools.integrationtest.HttpTunnelIntegrationTests.TunnelConfiguration.TestTunnelClient; import org.springframework.boot.devtools.remote.server.AccessManager; import org.springframework.boot.devtools.remote.server.Dispatcher; import org.springframework.boot.devtools.remote.server.DispatcherFilter; @@ -33,22 +34,19 @@ import org.springframework.boot.devtools.tunnel.client.TunnelClient; import org.springframework.boot.devtools.tunnel.client.TunnelConnection; import org.springframework.boot.devtools.tunnel.server.HttpTunnelServer; import org.springframework.boot.devtools.tunnel.server.HttpTunnelServerHandler; -import org.springframework.boot.devtools.tunnel.server.PortProvider; import org.springframework.boot.devtools.tunnel.server.SocketTargetServerConnection; -import org.springframework.boot.devtools.tunnel.server.StaticPortProvider; import org.springframework.boot.devtools.tunnel.server.TargetServerConnection; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.client.SimpleClientHttpRequestFactory; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.SocketUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.DispatcherServlet; @@ -61,48 +59,65 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Phillip Webb */ -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public class HttpTunnelIntegrationTests { - @Autowired - private Config config; - @Test public void httpServerDirect() throws Exception { - String url = "http://localhost:" + this.config.httpServerPort + "/hello"; + AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(); + context.register(ServerConfiguration.class); + context.refresh(); + String url = "http://localhost:" + context.getWebServer().getPort() + "/hello"; ResponseEntity entity = new TestRestTemplate().getForEntity(url, String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("Hello World"); + context.close(); } @Test public void viaTunnel() throws Exception { - String url = "http://localhost:" + this.config.clientPort + "/hello"; + AnnotationConfigServletWebServerApplicationContext serverContext = new AnnotationConfigServletWebServerApplicationContext(); + serverContext.register(ServerConfiguration.class); + serverContext.refresh(); + AnnotationConfigApplicationContext tunnelContext = new AnnotationConfigApplicationContext(); + TestPropertyValues.of("server.port:" + serverContext.getWebServer().getPort()) + .applyTo(tunnelContext); + tunnelContext.register(TunnelConfiguration.class); + tunnelContext.refresh(); + String url = "http://localhost:" + + tunnelContext.getBean(TestTunnelClient.class).port + "/hello"; ResponseEntity entity = new TestRestTemplate().getForEntity(url, String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("Hello World"); + serverContext.close(); + tunnelContext.close(); } @Configuration @EnableWebMvc - static class Config { + static class ServerConfiguration { - private int clientPort = SocketUtils.findAvailableTcpPort(); + @Bean + public ServletWebServerFactory container() { + return new TomcatServletWebServerFactory(0); + } - private int httpServerPort = SocketUtils.findAvailableTcpPort(); + @Bean + public DispatcherServlet dispatcherServlet() { + return new DispatcherServlet(); + } @Bean - public ServletWebServerFactory container() { - return new TomcatServletWebServerFactory(this.httpServerPort); + public MyController myController() { + return new MyController(); } @Bean - public DispatcherFilter filter() { - PortProvider port = new StaticPortProvider(this.httpServerPort); - TargetServerConnection connection = new SocketTargetServerConnection(port); + public DispatcherFilter filter( + AnnotationConfigServletWebServerApplicationContext context) { + TargetServerConnection connection = new SocketTargetServerConnection( + () -> context.getWebServer().getPort()); HttpTunnelServer server = new HttpTunnelServer(connection); HandlerMapper mapper = new UrlHandlerMapper("/httptunnel", new HttpTunnelServerHandler(server)); @@ -111,22 +126,32 @@ public class HttpTunnelIntegrationTests { return new DispatcherFilter(dispatcher); } + } + + static class TunnelConfiguration { + @Bean - public TunnelClient tunnelClient() { - String url = "http://localhost:" + this.httpServerPort + "/httptunnel"; + public TunnelClient tunnelClient(@Value("${server.port}") int serverPort) { + String url = "http://localhost:" + serverPort + "/httptunnel"; TunnelConnection connection = new HttpTunnelConnection(url, new SimpleClientHttpRequestFactory()); - return new TunnelClient(this.clientPort, connection); + return new TestTunnelClient(0, connection); } - @Bean - public DispatcherServlet dispatcherServlet() { - return new DispatcherServlet(); - } + static class TestTunnelClient extends TunnelClient { + + private int port; + + TestTunnelClient(int listenPort, TunnelConnection tunnelConnection) { + super(listenPort, tunnelConnection); + } + + @Override + public int start() throws IOException { + this.port = super.start(); + return this.port; + } - @Bean - public MyController myController() { - return new MyController(); } } diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java index f2df9ec91a..de975ac1ec 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/livereload/LiveReloadServerTests.java @@ -31,7 +31,6 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import org.springframework.util.SocketUtils; import org.springframework.web.client.RestTemplate; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.PingMessage; @@ -56,14 +55,14 @@ public class LiveReloadServerTests { private static final String HANDSHAKE = "{command: 'hello', " + "protocols: ['http://livereload.com/protocols/official-7']}"; - private int port = SocketUtils.findAvailableTcpPort(); + private int port; private MonitoredLiveReloadServer server; @Before public void setUp() throws Exception { - this.server = new MonitoredLiveReloadServer(this.port); - this.server.start(); + this.server = new MonitoredLiveReloadServer(0); + this.port = this.server.start(); } @After diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/LocalDebugPortAvailableConditionTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/LocalDebugPortAvailableConditionTests.java index a736c38683..0542fdf93e 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/LocalDebugPortAvailableConditionTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/LocalDebugPortAvailableConditionTests.java @@ -26,7 +26,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.annotation.ConditionContext; import org.springframework.mock.env.MockEnvironment; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; @@ -39,13 +38,11 @@ import static org.mockito.Mockito.mock; */ public class LocalDebugPortAvailableConditionTests { - private int port = SocketUtils.findAvailableTcpPort(); - private LocalDebugPortAvailableCondition condition = new LocalDebugPortAvailableCondition(); @Test public void portAvailable() throws Exception { - ConditionOutcome outcome = getOutcome(); + ConditionOutcome outcome = getOutcome(0); assertThat(outcome.isMatch()).isTrue(); assertThat(outcome.getMessage()) .isEqualTo("Local Debug Port Condition found local debug port"); @@ -53,19 +50,19 @@ public class LocalDebugPortAvailableConditionTests { @Test public void portInUse() throws Exception { - final ServerSocket serverSocket = ServerSocketFactory.getDefault() - .createServerSocket(this.port); - ConditionOutcome outcome = getOutcome(); + ServerSocket serverSocket = ServerSocketFactory.getDefault() + .createServerSocket(0); + ConditionOutcome outcome = getOutcome(serverSocket.getLocalPort()); serverSocket.close(); assertThat(outcome.isMatch()).isFalse(); assertThat(outcome.getMessage()) .isEqualTo("Local Debug Port Condition did not find local debug port"); } - private ConditionOutcome getOutcome() { + private ConditionOutcome getOutcome(int port) { MockEnvironment environment = new MockEnvironment(); - TestPropertyValues.of( - "spring.devtools.remote.debug.local-port:" + this.port).applyTo(environment); + TestPropertyValues.of("spring.devtools.remote.debug.local-port:" + port) + .applyTo(environment); ConditionContext context = mock(ConditionContext.class); given(context.getEnvironment()).willReturn(environment); ConditionOutcome outcome = this.condition.getMatchOutcome(context, null); diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/RemoteClientConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/RemoteClientConfigurationTests.java index 4297bd6a3c..c087907f45 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/RemoteClientConfigurationTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/remote/client/RemoteClientConfigurationTests.java @@ -43,11 +43,11 @@ import org.springframework.boot.test.rule.OutputCapture; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -73,13 +73,16 @@ public class RemoteClientConfigurationTests { private AnnotationConfigServletWebServerApplicationContext context; - private static int remotePort = SocketUtils.findAvailableTcpPort(); + private AnnotationConfigApplicationContext clientContext; @After public void cleanup() { if (this.context != null) { this.context.close(); } + if (this.clientContext != null) { + this.clientContext.close(); + } } @Test @@ -114,12 +117,12 @@ public class RemoteClientConfigurationTests { configure(); Set changeSet = new HashSet<>(); ClassPathChangedEvent event = new ClassPathChangedEvent(this, changeSet, false); - this.context.publishEvent(event); - LiveReloadConfiguration configuration = this.context + this.clientContext.publishEvent(event); + LiveReloadConfiguration configuration = this.clientContext .getBean(LiveReloadConfiguration.class); configuration.getExecutor().shutdown(); configuration.getExecutor().awaitTermination(2, TimeUnit.SECONDS); - LiveReloadServer server = this.context.getBean(LiveReloadServer.class); + LiveReloadServer server = this.clientContext.getBean(LiveReloadServer.class); verify(server).triggerReload(); } @@ -150,17 +153,24 @@ public class RemoteClientConfigurationTests { private void configure(String remoteUrl, boolean setSecret, String... pairs) { this.context = new AnnotationConfigServletWebServerApplicationContext(); - new RestartScopeInitializer().initialize(this.context); - this.context.register(Config.class, RemoteClientConfiguration.class); - String remoteUrlProperty = "remoteUrl:" + remoteUrl + ":" - + RemoteClientConfigurationTests.remotePort; - TestPropertyValues.of(remoteUrlProperty).applyTo(this.context); - TestPropertyValues.of(pairs).applyTo(this.context); + this.context.register(Config.class); if (setSecret) { - TestPropertyValues.of( - "spring.devtools.remote.secret:secret").applyTo(this.context); + TestPropertyValues.of("spring.devtools.remote.secret:secret") + .applyTo(this.context); } this.context.refresh(); + this.clientContext = new AnnotationConfigApplicationContext(); + TestPropertyValues.of(pairs).applyTo(this.clientContext); + new RestartScopeInitializer().initialize(this.clientContext); + this.clientContext.register(ClientConfig.class, RemoteClientConfiguration.class); + if (setSecret) { + TestPropertyValues.of("spring.devtools.remote.secret:secret") + .applyTo(this.clientContext); + } + String remoteUrlProperty = "remoteUrl:" + remoteUrl + ":" + + this.context.getWebServer().getPort(); + TestPropertyValues.of(remoteUrlProperty).applyTo(this.clientContext); + this.clientContext.refresh(); } @Configuration @@ -168,12 +178,7 @@ public class RemoteClientConfigurationTests { @Bean public TomcatServletWebServerFactory tomcat() { - return new TomcatServletWebServerFactory(remotePort); - } - - @Bean - public LiveReloadServer liveReloadServer() { - return mock(LiveReloadServer.class); + return new TomcatServletWebServerFactory(0); } @Bean @@ -191,4 +196,14 @@ public class RemoteClientConfigurationTests { } + @Configuration + static class ClientConfig { + + @Bean + public LiveReloadServer liveReloadServer() { + return mock(LiveReloadServer.class); + } + + } + } diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/HttpTunnelConnectionTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/HttpTunnelConnectionTests.java index 36b56fb72d..0ae7402081 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/HttpTunnelConnectionTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/HttpTunnelConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,6 @@ import org.springframework.boot.devtools.test.MockClientHttpRequestFactory; import org.springframework.boot.devtools.tunnel.client.HttpTunnelConnection.TunnelChannel; import org.springframework.boot.test.rule.OutputCapture; import org.springframework.http.HttpStatus; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; @@ -59,8 +58,6 @@ public class HttpTunnelConnectionTests { @Rule public OutputCapture outputCapture = new OutputCapture(); - private int port = SocketUtils.findAvailableTcpPort(); - private String url; private ByteArrayOutputStream incomingData; @@ -75,7 +72,7 @@ public class HttpTunnelConnectionTests { @Before public void setup() { MockitoAnnotations.initMocks(this); - this.url = "http://localhost:" + this.port; + this.url = "http://localhost:12345"; this.incomingData = new ByteArrayOutputStream(); this.incomingChannel = Channels.newChannel(this.incomingData); } @@ -164,8 +161,7 @@ public class HttpTunnelConnectionTests { TunnelChannel tunnel = openTunnel(true); assertThat(tunnel.isOpen()).isFalse(); this.outputCapture.expect(containsString( - "Failed to connect to remote application at http://localhost:" - + this.port)); + "Failed to connect to remote application at http://localhost:12345")); } private void write(TunnelChannel channel, String string) throws IOException { diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/TunnelClientTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/TunnelClientTests.java index da375b026f..546c9f906d 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/TunnelClientTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/client/TunnelClientTests.java @@ -29,8 +29,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.springframework.util.SocketUtils; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -46,15 +44,13 @@ public class TunnelClientTests { @Rule public ExpectedException thrown = ExpectedException.none(); - private int listenPort = SocketUtils.findAvailableTcpPort(); - private MockTunnelConnection tunnelConnection = new MockTunnelConnection(); @Test - public void listenPortMustBePositive() throws Exception { + public void listenPortMustNotBeNegative() throws Exception { this.thrown.expect(IllegalArgumentException.class); - this.thrown.expectMessage("ListenPort must be positive"); - new TunnelClient(0, this.tunnelConnection); + this.thrown.expectMessage("ListenPort must be greater than or equal to 0"); + new TunnelClient(-5, this.tunnelConnection); } @Test @@ -66,10 +62,9 @@ public class TunnelClientTests { @Test public void typicalTraffic() throws Exception { - TunnelClient client = new TunnelClient(this.listenPort, this.tunnelConnection); - client.start(); - SocketChannel channel = SocketChannel - .open(new InetSocketAddress(this.listenPort)); + TunnelClient client = new TunnelClient(0, this.tunnelConnection); + int port = client.start(); + SocketChannel channel = SocketChannel.open(new InetSocketAddress(port)); channel.write(ByteBuffer.wrap("hello".getBytes())); ByteBuffer buffer = ByteBuffer.allocate(5); channel.read(buffer); @@ -80,10 +75,9 @@ public class TunnelClientTests { @Test public void socketChannelClosedTriggersTunnelClose() throws Exception { - TunnelClient client = new TunnelClient(this.listenPort, this.tunnelConnection); - client.start(); - SocketChannel channel = SocketChannel - .open(new InetSocketAddress(this.listenPort)); + TunnelClient client = new TunnelClient(0, this.tunnelConnection); + int port = client.start(); + SocketChannel channel = SocketChannel.open(new InetSocketAddress(port)); Thread.sleep(200); channel.close(); client.getServerThread().stopAcceptingConnections(); @@ -94,10 +88,9 @@ public class TunnelClientTests { @Test public void stopTriggersTunnelClose() throws Exception { - TunnelClient client = new TunnelClient(this.listenPort, this.tunnelConnection); - client.start(); - SocketChannel channel = SocketChannel - .open(new InetSocketAddress(this.listenPort)); + TunnelClient client = new TunnelClient(0, this.tunnelConnection); + int port = client.start(); + SocketChannel channel = SocketChannel.open(new InetSocketAddress(port)); Thread.sleep(200); client.stop(); assertThat(this.tunnelConnection.getOpenedTimes()).isEqualTo(1); @@ -107,12 +100,11 @@ public class TunnelClientTests { @Test public void addListener() throws Exception { - TunnelClient client = new TunnelClient(this.listenPort, this.tunnelConnection); + TunnelClient client = new TunnelClient(0, this.tunnelConnection); TunnelClientListener listener = mock(TunnelClientListener.class); client.addListener(listener); - client.start(); - SocketChannel channel = SocketChannel - .open(new InetSocketAddress(this.listenPort)); + int port = client.start(); + SocketChannel channel = SocketChannel.open(new InetSocketAddress(port)); Thread.sleep(200); channel.close(); client.getServerThread().stopAcceptingConnections(); diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/server/SocketTargetServerConnectionTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/server/SocketTargetServerConnectionTests.java index b54a7f3bb6..d9970494b4 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/server/SocketTargetServerConnectionTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/tunnel/server/SocketTargetServerConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,6 @@ import java.nio.channels.SocketChannel; import org.junit.Before; import org.junit.Test; -import org.springframework.util.SocketUtils; - import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -41,18 +39,14 @@ public class SocketTargetServerConnectionTests { private static final int DEFAULT_TIMEOUT = 1000; - private int port; - private MockServer server; private SocketTargetServerConnection connection; @Before public void setup() throws IOException { - this.port = SocketUtils.findAvailableTcpPort(); - this.server = new MockServer(this.port); - StaticPortProvider portProvider = new StaticPortProvider(this.port); - this.connection = new SocketTargetServerConnection(portProvider); + this.server = new MockServer(); + this.connection = new SocketTargetServerConnection(() -> this.server.getPort()); } @Test @@ -107,9 +101,13 @@ public class SocketTargetServerConnectionTests { private ServerThread thread; - MockServer(int port) throws IOException { + MockServer() throws IOException { this.serverSocket = ServerSocketChannel.open(); - this.serverSocket.bind(new InetSocketAddress(port)); + this.serverSocket.bind(new InetSocketAddress(0)); + } + + int getPort() { + return this.serverSocket.socket().getLocalPort(); } public void delay(int delay) { diff --git a/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java b/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java index 9958ada74a..c6b6094d05 100644 --- a/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java +++ b/spring-boot-integration-tests/spring-boot-devtools-tests/src/test/java/org/springframework/boot/devtools/tests/RemoteApplicationLauncher.java @@ -17,13 +17,14 @@ package org.springframework.boot.devtools.tests; import java.io.File; +import java.io.FileReader; import java.util.ArrayList; import java.util.List; import org.springframework.boot.devtools.RemoteSpringApplication; import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm; +import org.springframework.util.FileCopyUtils; import org.springframework.util.FileSystemUtils; -import org.springframework.util.SocketUtils; import org.springframework.util.StringUtils; /** @@ -37,14 +38,15 @@ abstract class RemoteApplicationLauncher implements ApplicationLauncher { @Override public LaunchedApplication launchApplication(JvmLauncher javaLauncher) throws Exception { - int port = SocketUtils.findAvailableTcpPort(); LaunchedJvm applicationJvm = javaLauncher.launch("app", createApplicationClassPath(), "com.example.DevToolsTestApplication", - "--server.port=" + port, "--spring.devtools.remote.secret=secret"); + "--server.port=12345", "--spring.devtools.remote.secret=secret"); + awaitServerPort(applicationJvm.getStandardOut()); LaunchedJvm remoteSpringApplicationJvm = javaLauncher.launch( "remote-spring-application", createRemoteSpringApplicationClassPath(), RemoteSpringApplication.class.getName(), - "--spring.devtools.remote.secret=secret", "http://localhost:" + port); + "--spring.devtools.remote.secret=secret", "http://localhost:12345"); + awaitRemoteSpringApplication(remoteSpringApplicationJvm.getStandardOut()); return new LaunchedApplication(new File("target/remote"), applicationJvm.getStandardOut(), applicationJvm.getProcess(), remoteSpringApplicationJvm.getProcess()); @@ -66,4 +68,40 @@ abstract class RemoteApplicationLauncher implements ApplicationLauncher { return StringUtils.collectionToDelimitedString(entries, File.pathSeparator); } + private int awaitServerPort(File standardOut) throws Exception { + long end = System.currentTimeMillis() + 30000; + File serverPortFile = new File("target/server.port"); + while (serverPortFile.length() == 0) { + if (System.currentTimeMillis() > end) { + throw new IllegalStateException(String.format( + "server.port file was not written within 30 seconds. " + + "Application output:%n%s", + FileCopyUtils.copyToString(new FileReader(standardOut)))); + } + Thread.sleep(100); + } + FileReader portReader = new FileReader(serverPortFile); + int port = Integer.valueOf(FileCopyUtils.copyToString(portReader)); + return port; + } + + private void awaitRemoteSpringApplication(File standardOut) throws Exception { + long end = System.currentTimeMillis() + 30000; + while (!standardOut.exists()) { + if (System.currentTimeMillis() > end) { + throw new IllegalStateException( + "Standard out file was not written " + "within 30 seconds"); + } + Thread.sleep(100); + } + while (!FileCopyUtils.copyToString(new FileReader(standardOut)) + .contains("Started RemoteSpringApplication")) { + if (System.currentTimeMillis() > end) { + throw new IllegalStateException( + "RemoteSpringApplication did not start within 30 seconds"); + } + Thread.sleep(100); + } + } + } diff --git a/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/main/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplication.java b/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/main/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplication.java index 255dba59f8..54b75006c6 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/main/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplication.java +++ b/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/main/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplication.java @@ -23,7 +23,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.Bean; -import org.springframework.util.SocketUtils; /** * Sample Application to show Tomcat running two connectors @@ -34,11 +33,6 @@ import org.springframework.util.SocketUtils; @SpringBootApplication public class SampleTomcatTwoConnectorsApplication { - @Bean - public Integer port() { - return SocketUtils.findAvailableTcpPort(); - } - @Bean public ServletWebServerFactory servletContainer() { TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); @@ -48,7 +42,7 @@ public class SampleTomcatTwoConnectorsApplication { private Connector createStandardConnector() { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); - connector.setPort(port()); + connector.setPort(0); return connector; } diff --git a/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/test/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplicationTests.java b/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/test/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplicationTests.java index e72978ad98..0c4c6d3f4d 100644 --- a/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/test/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-tomcat-multi-connectors/src/test/java/sample/tomcat/multiconnector/SampleTomcatTwoConnectorsApplicationTests.java @@ -22,19 +22,26 @@ import java.net.HttpURLConnection; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import org.apache.catalina.Service; +import org.apache.catalina.connector.Connector; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import sample.tomcat.multiconnector.SampleTomcatTwoConnectorsApplicationTests.Ports; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.web.context.WebServerInitializedEvent; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; import org.springframework.boot.web.server.LocalServerPort; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Import; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.client.SimpleClientHttpRequestFactory; @@ -53,10 +60,11 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @DirtiesContext +@Import(Ports.class) public class SampleTomcatTwoConnectorsApplicationTests { @LocalServerPort - private String port; + private int port; @Autowired private ApplicationContext context; @@ -97,27 +105,19 @@ public class SampleTomcatTwoConnectorsApplicationTests { public void testHello() throws Exception { RestTemplate template = new RestTemplate(); final MySimpleClientHttpRequestFactory factory = new MySimpleClientHttpRequestFactory( - new HostnameVerifier() { - - @Override - public boolean verify(final String hostname, - final SSLSession session) { - return true; // these guys are alright by me... - } - }); + (hostname, session) -> true); template.setRequestFactory(factory); - + Ports ports = this.context.getBean(Ports.class); + assertThat(ports.getHttpsPort()).isEqualTo(this.port); + assertThat(ports.getHttpPort()).isNotEqualTo(this.port); ResponseEntity entity = template.getForEntity( - "http://localhost:" + this.context.getBean("port") + "/hello", - String.class); + "http://localhost:" + ports.getHttpPort() + "/hello", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("hello"); - ResponseEntity httpsEntity = template .getForEntity("https://localhost:" + this.port + "/hello", String.class); assertThat(httpsEntity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(httpsEntity.getBody()).isEqualTo("hello"); - } /** @@ -141,4 +141,35 @@ public class SampleTomcatTwoConnectorsApplicationTests { } } + @TestConfiguration + static class Ports implements ApplicationListener { + + private int httpPort; + + private int httpsPort; + + @Override + public void onApplicationEvent(WebServerInitializedEvent event) { + Service service = ((TomcatWebServer) event.getWebServer()).getTomcat() + .getService(); + for (Connector connector : service.findConnectors()) { + if (connector.getSecure()) { + this.httpsPort = connector.getLocalPort(); + } + else { + this.httpPort = connector.getLocalPort(); + } + } + } + + int getHttpPort() { + return this.httpPort; + } + + int getHttpsPort() { + return this.httpsPort; + } + + } + } diff --git a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/test/java/samples/websocket/jetty/echo/CustomContainerWebSocketsApplicationTests.java b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/test/java/samples/websocket/jetty/echo/CustomContainerWebSocketsApplicationTests.java index 912391a1c0..1e875f1f40 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-jetty/src/test/java/samples/websocket/jetty/echo/CustomContainerWebSocketsApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-websocket-jetty/src/test/java/samples/websocket/jetty/echo/CustomContainerWebSocketsApplicationTests.java @@ -37,13 +37,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; +import org.springframework.boot.web.server.LocalServerPort; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.SocketUtils; import org.springframework.web.socket.client.WebSocketConnectionManager; import org.springframework.web.socket.client.standard.StandardWebSocketClient; @@ -51,20 +51,21 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(classes = { SampleJettyWebSocketsApplication.class, - CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT) + CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.RANDOM_PORT) @DirtiesContext public class CustomContainerWebSocketsApplicationTests { private static Log logger = LogFactory .getLog(CustomContainerWebSocketsApplicationTests.class); - private static int PORT = SocketUtils.findAvailableTcpPort(); + @LocalServerPort + private int port; @Test public void echoEndpoint() throws Exception { ConfigurableApplicationContext context = new SpringApplicationBuilder( ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .properties("websocket.uri:ws://localhost:" + PORT + .properties("websocket.uri:ws://localhost:" + this.port + "/ws/echo/websocket") .run("--spring.main.web_environment=false"); long count = context.getBean(ClientConfiguration.class).latch.getCount(); @@ -80,8 +81,8 @@ public class CustomContainerWebSocketsApplicationTests { public void reverseEndpoint() throws Exception { ConfigurableApplicationContext context = new SpringApplicationBuilder( ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .properties( - "websocket.uri:ws://localhost:" + PORT + "/ws/reverse") + .properties("websocket.uri:ws://localhost:" + this.port + + "/ws/reverse") .run("--spring.main.web_environment=false"); long count = context.getBean(ClientConfiguration.class).latch.getCount(); AtomicReference messagePayloadReference = context @@ -96,7 +97,7 @@ public class CustomContainerWebSocketsApplicationTests { @Bean public ServletWebServerFactory webServerFactory() { - return new JettyServletWebServerFactory("/ws", PORT); + return new JettyServletWebServerFactory("/ws", 0); } } diff --git a/spring-boot-samples/spring-boot-sample-websocket-tomcat/src/test/java/samples/websocket/tomcat/echo/CustomContainerWebSocketsApplicationTests.java b/spring-boot-samples/spring-boot-sample-websocket-tomcat/src/test/java/samples/websocket/tomcat/echo/CustomContainerWebSocketsApplicationTests.java index ad99a63bbf..085ac0b62b 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-tomcat/src/test/java/samples/websocket/tomcat/echo/CustomContainerWebSocketsApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-websocket-tomcat/src/test/java/samples/websocket/tomcat/echo/CustomContainerWebSocketsApplicationTests.java @@ -37,13 +37,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.LocalServerPort; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.SocketUtils; import org.springframework.web.socket.client.WebSocketConnectionManager; import org.springframework.web.socket.client.standard.StandardWebSocketClient; @@ -51,20 +51,21 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(classes = { SampleTomcatWebSocketApplication.class, - CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT) + CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.RANDOM_PORT) @DirtiesContext public class CustomContainerWebSocketsApplicationTests { private static Log logger = LogFactory .getLog(CustomContainerWebSocketsApplicationTests.class); - private static int PORT = SocketUtils.findAvailableTcpPort(); + @LocalServerPort + private int port; @Test public void echoEndpoint() throws Exception { ConfigurableApplicationContext context = new SpringApplicationBuilder( ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .properties("websocket.uri:ws://localhost:" + PORT + .properties("websocket.uri:ws://localhost:" + this.port + "/ws/echo/websocket") .run("--spring.main.web_environment=false"); long count = context.getBean(ClientConfiguration.class).latch.getCount(); @@ -80,8 +81,8 @@ public class CustomContainerWebSocketsApplicationTests { public void reverseEndpoint() throws Exception { ConfigurableApplicationContext context = new SpringApplicationBuilder( ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .properties( - "websocket.uri:ws://localhost:" + PORT + "/ws/reverse") + .properties("websocket.uri:ws://localhost:" + this.port + + "/ws/reverse") .run("--spring.main.web_environment=false"); long count = context.getBean(ClientConfiguration.class).latch.getCount(); AtomicReference messagePayloadReference = context @@ -96,7 +97,7 @@ public class CustomContainerWebSocketsApplicationTests { @Bean public ServletWebServerFactory webServerFactory() { - return new TomcatServletWebServerFactory("/ws", PORT); + return new TomcatServletWebServerFactory("/ws", 0); } } diff --git a/spring-boot-samples/spring-boot-sample-websocket-undertow/src/test/java/samples/websocket/undertow/echo/CustomContainerWebSocketsApplicationTests.java b/spring-boot-samples/spring-boot-sample-websocket-undertow/src/test/java/samples/websocket/undertow/echo/CustomContainerWebSocketsApplicationTests.java index 78d1e12cbf..6e37eb9ca3 100644 --- a/spring-boot-samples/spring-boot-sample-websocket-undertow/src/test/java/samples/websocket/undertow/echo/CustomContainerWebSocketsApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-websocket-undertow/src/test/java/samples/websocket/undertow/echo/CustomContainerWebSocketsApplicationTests.java @@ -37,13 +37,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.server.LocalServerPort; import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.SocketUtils; import org.springframework.web.socket.client.WebSocketConnectionManager; import org.springframework.web.socket.client.standard.StandardWebSocketClient; @@ -51,20 +51,21 @@ import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest(classes = { SampleUndertowWebSocketsApplication.class, - CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT) + CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.RANDOM_PORT) @DirtiesContext public class CustomContainerWebSocketsApplicationTests { private static Log logger = LogFactory .getLog(CustomContainerWebSocketsApplicationTests.class); - private static int PORT = SocketUtils.findAvailableTcpPort(); + @LocalServerPort + private int port; @Test public void echoEndpoint() throws Exception { ConfigurableApplicationContext context = new SpringApplicationBuilder( ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .properties("websocket.uri:ws://localhost:" + PORT + .properties("websocket.uri:ws://localhost:" + this.port + "/ws/echo/websocket") .run("--spring.main.web_environment=false"); long count = context.getBean(ClientConfiguration.class).latch.getCount(); @@ -80,8 +81,8 @@ public class CustomContainerWebSocketsApplicationTests { public void reverseEndpoint() throws Exception { ConfigurableApplicationContext context = new SpringApplicationBuilder( ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) - .properties( - "websocket.uri:ws://localhost:" + PORT + "/ws/reverse") + .properties("websocket.uri:ws://localhost:" + this.port + + "/ws/reverse") .run("--spring.main.web_environment=false"); long count = context.getBean(ClientConfiguration.class).latch.getCount(); AtomicReference messagePayloadReference = context @@ -96,7 +97,7 @@ public class CustomContainerWebSocketsApplicationTests { @Bean public ServletWebServerFactory webServerFactory() { - return new UndertowServletWebServerFactory("/ws", PORT); + return new UndertowServletWebServerFactory("/ws", 0); } } diff --git a/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java index 8dd392b801..18751ecf05 100644 --- a/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java @@ -54,7 +54,6 @@ import org.springframework.boot.web.server.WebServerException; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.SocketUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -92,7 +91,7 @@ public class TomcatServletWebServerFactoryTests public void tomcatEngineNames() throws Exception { TomcatServletWebServerFactory factory = getFactory(); this.webServer = factory.getWebServer(); - factory.setPort(SocketUtils.findAvailableTcpPort(40000)); + factory.setPort(0); TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer(); // Make sure that the names are different String firstName = ((TomcatWebServer) this.webServer).getTomcat().getEngine()