Minimise our usage of SocketUtils.findAvailableTcpPort

Closes gh-9382
pull/10624/head
Andy Wilkinson 8 years ago
parent 10868519e1
commit f7e9ec5f42

@ -17,8 +17,6 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketException; import java.net.SocketException;
import java.net.URI; import java.net.URI;
import java.nio.charset.Charset; 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.test.util.TestPropertyValues;
import org.springframework.boot.testutil.Matched; import org.springframework.boot.testutil.Matched;
import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer; 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.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; 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.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest; 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.http.client.SimpleClientHttpRequestFactory;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.SocketUtils;
import org.springframework.util.StreamUtils; import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -134,7 +130,6 @@ public class EndpointWebMvcAutoConfigurationTests {
@After @After
public void cleanUp() throws Exception { public void cleanUp() throws Exception {
this.applicationContext.close(); this.applicationContext.close();
assertAllClosed();
} }
@Test @Test
@ -146,8 +141,6 @@ public class EndpointWebMvcAutoConfigurationTests {
this.applicationContext.refresh(); this.applicationContext.refresh();
assertContent("/controller", ports.get().server, "controlleroutput"); assertContent("/controller", ports.get().server, "controlleroutput");
assertContent("/endpoint", ports.get().server, "endpointoutput"); 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")) assertThat(hasHeader("/endpoint", ports.get().server, "X-Application-Context"))
.isFalse(); .isFalse();
assertThat(this.applicationContext.containsBean("applicationContextIdFilter")) assertThat(this.applicationContext.containsBean("applicationContextIdFilter"))
@ -274,25 +267,6 @@ public class EndpointWebMvcAutoConfigurationTests {
assertContent("/endpoint", ports.get().management, null); 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 @Test
public void onDifferentPortWithPrimaryFailure() throws Exception { public void onDifferentPortWithPrimaryFailure() throws Exception {
TestPropertyValues.of("management.port=" + ports.get().management) TestPropertyValues.of("management.port=" + ports.get().management)
@ -340,10 +314,7 @@ public class EndpointWebMvcAutoConfigurationTests {
} }
@Test @Test
public void specificPortsViaPropertiesWithClash() throws Exception { public void managementContextFailureCausesMainContextFailure() throws Exception {
int managementPort = ports.get().management;
try (ServerSocket serverSocket = new ServerSocket()) {
serverSocket.bind(new InetSocketAddress(managementPort));
TestPropertyValues TestPropertyValues
.of("server.port:" + ports.get().server, .of("server.port:" + ports.get().server,
"management.port:" + ports.get().management) "management.port:" + ports.get().management)
@ -351,9 +322,14 @@ public class EndpointWebMvcAutoConfigurationTests {
this.applicationContext.register(RootConfig.class, EndpointConfig.class, this.applicationContext.register(RootConfig.class, EndpointConfig.class,
BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class, BaseConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class); ErrorMvcAutoConfiguration.class);
this.thrown.expect(WebServerException.class); this.applicationContext.addApplicationListener(
this.applicationContext.refresh(); (ApplicationListener<ContextRefreshedEvent>) (event) -> {
if (event.getApplicationContext().getParent() != null) {
throw new RuntimeException();
} }
});
this.thrown.expect(RuntimeException.class);
this.applicationContext.refresh();
} }
@Test @Test
@ -671,13 +647,6 @@ public class EndpointWebMvcAutoConfigurationTests {
assertThat(this.applicationContext.getBeansOfType(type)).hasSize(1); 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) private void assertHttpsContent(String url, int port, Object expected)
throws Exception { throws Exception {
assertContent("https", url, port, expected); assertContent("https", url, port, expected);
@ -733,9 +702,9 @@ public class EndpointWebMvcAutoConfigurationTests {
private static class Ports { 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(); return new TestController();
} }
@Bean
public ApplicationListener<WebServerInitializedEvent> serverPortListener() {
return (event) -> {
int port = event.getWebServer().getPort();
if (event.getApplicationContext().getParent() == null) {
ports.get().server = port;
}
else {
ports.get().management = port;
}
};
}
} }
@Configuration @Configuration
@ -886,30 +868,6 @@ public class EndpointWebMvcAutoConfigurationTests {
} }
private static class GrabManagementPort
implements ApplicationListener<ServletWebServerInitializedEvent> {
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 private static class SpecificServletWebServerFactory
extends TomcatServletWebServerFactory { extends TomcatServletWebServerFactory {

@ -57,7 +57,6 @@ import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ConnectionCallback; import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -347,9 +346,7 @@ public class PublicMetricsAutoConfigurationTests {
@Bean @Bean
public TomcatServletWebServerFactory webServerFactory() { public TomcatServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); return new TomcatServletWebServerFactory(0);
factory.setPort(SocketUtils.findAvailableTcpPort(40000));
return factory;
} }
} }

@ -25,7 +25,6 @@ import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactor
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -55,9 +54,7 @@ public class TomcatPublicMetricsTests {
@Bean @Bean
public TomcatServletWebServerFactory webServerFactory() { public TomcatServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); return new TomcatServletWebServerFactory(0);
factory.setPort(SocketUtils.findAvailableTcpPort(40000));
return factory;
} }
@Bean @Bean

@ -28,7 +28,6 @@ import org.junit.Test;
import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.writer.Delta; import org.springframework.boot.actuate.metrics.writer.Delta;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -40,12 +39,10 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
public class StatsdMetricWriterTests { public class StatsdMetricWriterTests {
private int port = SocketUtils.findAvailableTcpPort(); private DummyStatsDServer server = new DummyStatsDServer(0);
private DummyStatsDServer server = new DummyStatsDServer(this.port);
private StatsdMetricWriter writer = new StatsdMetricWriter("me", "localhost", private StatsdMetricWriter writer = new StatsdMetricWriter("me", "localhost",
this.port); this.server.getPort());
@After @After
public void close() { public void close() {
@ -84,7 +81,7 @@ public class StatsdMetricWriterTests {
@Test @Test
public void nullPrefix() throws Exception { 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.writer.set(new Metric<>("gauge.foo", 3L));
this.server.waitForMessage(); this.server.waitForMessage();
assertThat(this.server.messagesReceived().get(0)).isEqualTo("gauge.foo:3|g"); assertThat(this.server.messagesReceived().get(0)).isEqualTo("gauge.foo:3|g");
@ -92,7 +89,7 @@ public class StatsdMetricWriterTests {
@Test @Test
public void periodPrefix() throws Exception { 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.writer.set(new Metric<>("gauge.foo", 3L));
this.server.waitForMessage(); this.server.waitForMessage();
assertThat(this.server.messagesReceived().get(0)).isEqualTo("my.gauge.foo:3|g"); assertThat(this.server.messagesReceived().get(0)).isEqualTo("my.gauge.foo:3|g");
@ -129,6 +126,10 @@ public class StatsdMetricWriterTests {
new Thread(this).start(); new Thread(this).start();
} }
int getPort() {
return this.server.getLocalPort();
}
public void stop() { public void stop() {
this.server.close(); this.server.close();
} }

@ -16,7 +16,11 @@
package org.springframework.boot.autoconfigure.elasticsearch.jest; package org.springframework.boot.autoconfigure.elasticsearch.jest;
import java.io.File;
import java.io.IOException; 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.HashMap;
import java.util.Map; import java.util.Map;
@ -26,6 +30,7 @@ import io.searchbox.client.config.HttpClientConfig;
import io.searchbox.client.http.JestHttpClient; import io.searchbox.client.http.JestHttpClient;
import io.searchbox.core.Index; import io.searchbox.core.Index;
import io.searchbox.core.Search; import io.searchbox.core.Search;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.After; import org.junit.After;
@ -33,15 +38,20 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; 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.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration; import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues; 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.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -105,12 +115,12 @@ public class JestAutoConfigurationTests {
@Test @Test
public void jestCanCommunicateWithElasticsearchInstance() throws IOException { public void jestCanCommunicateWithElasticsearchInstance() throws IOException {
int port = SocketUtils.findAvailableTcpPort(); new File("target/elastic/logs").mkdirs();
load(ElasticsearchAutoConfiguration.class, load(HttpPortConfiguration.class,
"spring.data.elasticsearch.properties.path.home:target/elastic", "spring.data.elasticsearch.properties.path.home:target/elastic",
"spring.data.elasticsearch.properties.http.enabled:true", "spring.data.elasticsearch.properties.http.enabled:true",
"spring.data.elasticsearch.properties.http.port:" + port, "spring.data.elasticsearch.properties.http.port:0",
"spring.elasticsearch.jest.uris:http://localhost:" + port); "spring.data.elasticsearch.properties.node.portsfile:true");
JestClient client = this.context.getBean(JestClient.class); JestClient client = this.context.getBean(JestClient.class);
Map<String, String> source = new HashMap<>(); Map<String, String> source = new HashMap<>();
source.put("a", "alpha"); 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);
}
}
}
}
} }

@ -38,7 +38,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.util.FileSystemUtils; import org.springframework.util.FileSystemUtils;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -157,9 +156,7 @@ public class EmbeddedMongoAutoConfigurationTests {
private void assertVersionConfiguration(String configuredVersion, private void assertVersionConfiguration(String configuredVersion,
String expectedVersion) { String expectedVersion) {
this.context = new AnnotationConfigApplicationContext(); this.context = new AnnotationConfigApplicationContext();
int mongoPort = SocketUtils.findAvailableTcpPort(); TestPropertyValues.of("spring.data.mongodb.port=0").applyTo(this.context);
TestPropertyValues.of("spring.data.mongodb.port=" + mongoPort)
.applyTo(this.context);
if (configuredVersion != null) { if (configuredVersion != null) {
TestPropertyValues.of("spring.mongodb.embedded.version=" + configuredVersion) TestPropertyValues.of("spring.mongodb.embedded.version=" + configuredVersion)
.applyTo(this.context); .applyTo(this.context);

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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;
import org.springframework.boot.cli.infrastructure.CommandLineInvoker.Invocation; import org.springframework.boot.cli.infrastructure.CommandLineInvoker.Invocation;
import org.springframework.boot.loader.tools.JavaExecutable; import org.springframework.boot.loader.tools.JavaExecutable;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -41,15 +40,13 @@ public class WarCommandIT {
@Test @Test
public void warCreation() throws Exception { public void warCreation() throws Exception {
int port = SocketUtils.findAvailableTcpPort();
File war = new File("target/test-app.war"); File war = new File("target/test-app.war");
Invocation invocation = this.cli.invoke("war", war.getAbsolutePath(), Invocation invocation = this.cli.invoke("war", war.getAbsolutePath(),
"war.groovy"); "war.groovy");
invocation.await(); invocation.await();
assertThat(war.exists()).isTrue(); assertThat(war.exists()).isTrue();
Process process = new JavaExecutable() Process process = new JavaExecutable()
.processBuilder("-jar", war.getAbsolutePath(), "--server.port=" + port) .processBuilder("-jar", war.getAbsolutePath(), "--server.port=0").start();
.start();
invocation = new Invocation(process); invocation = new Invocation(process);
invocation.await(); invocation.await();
assertThat(invocation.getOutput()).contains("onStart error"); assertThat(invocation.getOutput()).contains("onStart error");

@ -18,6 +18,7 @@ package org.springframework.boot.cli;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.reflect.Field; 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.grab.GrabCommand;
import org.springframework.boot.cli.command.run.RunCommand; import org.springframework.boot.cli.command.run.RunCommand;
import org.springframework.boot.test.rule.OutputCapture; 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. * {@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 String prefix;
private final int port = SocketUtils.findAvailableTcpPort();
public CliTester(String prefix) { public CliTester(String prefix) {
this.prefix = prefix; this.prefix = prefix;
} }
@ -71,7 +70,21 @@ public class CliTester implements TestRule {
} }
public String run(String... args) throws Exception { public String run(String... args) throws Exception {
Future<RunCommand> future = submitCommand(new RunCommand(), args); List<String> 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<RunCommand> future = submitCommand(new RunCommand(),
updatedArgs.toArray(new String[updatedArgs.size()]));
this.commands.add(future.get(this.timeout, TimeUnit.MILLISECONDS)); this.commands.add(future.get(this.timeout, TimeUnit.MILLISECONDS));
return getOutput(); return getOutput();
} }
@ -96,13 +109,19 @@ public class CliTester implements TestRule {
@Override @Override
public T call() throws Exception { public T call() throws Exception {
ClassLoader loader = Thread.currentThread().getContextClassLoader(); 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 { try {
command.run(sources); command.run(sources);
return command; return command;
} }
finally { finally {
System.clearProperty("server.port"); System.clearProperty("server.port");
System.clearProperty("spring.application.class.name");
System.clearProperty("portfile");
Thread.currentThread().setContextClassLoader(loader); Thread.currentThread().setContextClassLoader(loader);
} }
} }
@ -137,7 +156,7 @@ public class CliTester implements TestRule {
} }
} }
else { else {
sources[i] = this.prefix + arg; sources[i] = new File(arg).isAbsolute() ? arg : this.prefix + arg;
} }
} }
return sources; return sources;
@ -172,7 +191,9 @@ public class CliTester implements TestRule {
public String getHttpOutput(String uri) { public String getHttpOutput(String uri) {
try { 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(); .openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line; String line;

@ -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());
}
}

@ -112,13 +112,15 @@ public class LiveReloadServer {
/** /**
* Start the livereload server and accept incoming connections. * 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 * @throws IOException in case of I/O errors
*/ */
public void start() throws IOException { public int start() throws IOException {
synchronized (this.monitor) { synchronized (this.monitor) {
Assert.state(!isStarted(), "Server already started"); Assert.state(!isStarted(), "Server already started");
logger.debug("Starting live reload server on port " + this.port); logger.debug("Starting live reload server on port " + this.port);
this.serverSocket = new ServerSocket(this.port); this.serverSocket = new ServerSocket(this.port);
int localPort = this.serverSocket.getLocalPort();
this.listenThread = this.threadFactory.newThread(new Runnable() { this.listenThread = this.threadFactory.newThread(new Runnable() {
@Override @Override
@ -130,6 +132,7 @@ public class LiveReloadServer {
this.listenThread.setDaemon(true); this.listenThread.setDaemon(true);
this.listenThread.setName("Live Reload Server"); this.listenThread.setName("Live Reload Server");
this.listenThread.start(); this.listenThread.start();
return localPort;
} }
} }

@ -57,7 +57,7 @@ public class TunnelClient implements SmartInitializingSingleton {
private ServerThread serverThread; private ServerThread serverThread;
public TunnelClient(int listenPort, TunnelConnection tunnelConnection) { 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"); Assert.notNull(tunnelConnection, "TunnelConnection must not be null");
this.listenPort = listenPort; this.listenPort = listenPort;
this.tunnelConnection = tunnelConnection; 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 * @throws IOException in case of I/O errors
*/ */
public void start() throws IOException { public int start() throws IOException {
synchronized (this.monitor) { synchronized (this.monitor) {
Assert.state(this.serverThread == null, "Server already started"); Assert.state(this.serverThread == null, "Server already started");
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(this.listenPort)); serverSocketChannel.socket().bind(new InetSocketAddress(this.listenPort));
logger.trace( int port = serverSocketChannel.socket().getLocalPort();
"Listening for TCP traffic to tunnel on port " + this.listenPort); logger.trace("Listening for TCP traffic to tunnel on port " + port);
this.serverThread = new ServerThread(serverSocketChannel); this.serverThread = new ServerThread(serverSocketChannel);
this.serverThread.start(); this.serverThread.start();
return port;
} }
} }
@ -100,7 +102,6 @@ public class TunnelClient implements SmartInitializingSingleton {
public void stop() throws IOException { public void stop() throws IOException {
synchronized (this.monitor) { synchronized (this.monitor) {
if (this.serverThread != null) { if (this.serverThread != null) {
logger.trace("Closing tunnel client on port " + this.listenPort);
this.serverThread.close(); this.serverThread.close();
try { try {
this.serverThread.join(2000); this.serverThread.join(2000);
@ -143,6 +144,8 @@ public class TunnelClient implements SmartInitializingSingleton {
} }
public void close() throws IOException { public void close() throws IOException {
logger.trace("Closing tunnel client on port "
+ this.serverSocketChannel.socket().getLocalPort());
this.serverSocketChannel.close(); this.serverSocketChannel.close();
this.acceptConnections = false; this.acceptConnections = false;
interrupt(); interrupt();

@ -53,7 +53,6 @@ import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@ -77,8 +76,6 @@ public class LocalDevToolsAutoConfigurationTests {
@Rule @Rule
public MockRestarter mockRestarter = new MockRestarter(); public MockRestarter mockRestarter = new MockRestarter();
private int liveReloadPort = SocketUtils.findAvailableTcpPort();
private ConfigurableApplicationContext context; private ConfigurableApplicationContext context;
@After @After
@ -270,7 +267,7 @@ public class LocalDevToolsAutoConfigurationTests {
Map<String, Object> specifiedProperties) { Map<String, Object> specifiedProperties) {
Map<String, Object> properties = new HashMap<>(); Map<String, Object> properties = new HashMap<>();
properties.put("spring.thymeleaf.check-template-location", false); 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.put("server.port", 0);
properties.putAll(specifiedProperties); properties.putAll(specifiedProperties);
return properties; return properties;

@ -16,13 +16,14 @@
package org.springframework.boot.devtools.integrationtest; package org.springframework.boot.devtools.integrationtest;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import org.junit.Test; 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.AccessManager;
import org.springframework.boot.devtools.remote.server.Dispatcher; import org.springframework.boot.devtools.remote.server.Dispatcher;
import org.springframework.boot.devtools.remote.server.DispatcherFilter; 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.client.TunnelConnection;
import org.springframework.boot.devtools.tunnel.server.HttpTunnelServer; import org.springframework.boot.devtools.tunnel.server.HttpTunnelServer;
import org.springframework.boot.devtools.tunnel.server.HttpTunnelServerHandler; 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.SocketTargetServerConnection;
import org.springframework.boot.devtools.tunnel.server.StaticPortProvider;
import org.springframework.boot.devtools.tunnel.server.TargetServerConnection; import org.springframework.boot.devtools.tunnel.server.TargetServerConnection;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; 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.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory; 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.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
@ -61,48 +59,65 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Phillip Webb * @author Phillip Webb
*/ */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class HttpTunnelIntegrationTests { public class HttpTunnelIntegrationTests {
@Autowired
private Config config;
@Test @Test
public void httpServerDirect() throws Exception { 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<String> entity = new TestRestTemplate().getForEntity(url, ResponseEntity<String> entity = new TestRestTemplate().getForEntity(url,
String.class); String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("Hello World"); assertThat(entity.getBody()).isEqualTo("Hello World");
context.close();
} }
@Test @Test
public void viaTunnel() throws Exception { 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<String> entity = new TestRestTemplate().getForEntity(url, ResponseEntity<String> entity = new TestRestTemplate().getForEntity(url,
String.class); String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("Hello World"); assertThat(entity.getBody()).isEqualTo("Hello World");
serverContext.close();
tunnelContext.close();
} }
@Configuration @Configuration
@EnableWebMvc @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 @Bean
public ServletWebServerFactory container() { public MyController myController() {
return new TomcatServletWebServerFactory(this.httpServerPort); return new MyController();
} }
@Bean @Bean
public DispatcherFilter filter() { public DispatcherFilter filter(
PortProvider port = new StaticPortProvider(this.httpServerPort); AnnotationConfigServletWebServerApplicationContext context) {
TargetServerConnection connection = new SocketTargetServerConnection(port); TargetServerConnection connection = new SocketTargetServerConnection(
() -> context.getWebServer().getPort());
HttpTunnelServer server = new HttpTunnelServer(connection); HttpTunnelServer server = new HttpTunnelServer(connection);
HandlerMapper mapper = new UrlHandlerMapper("/httptunnel", HandlerMapper mapper = new UrlHandlerMapper("/httptunnel",
new HttpTunnelServerHandler(server)); new HttpTunnelServerHandler(server));
@ -111,22 +126,32 @@ public class HttpTunnelIntegrationTests {
return new DispatcherFilter(dispatcher); return new DispatcherFilter(dispatcher);
} }
}
static class TunnelConfiguration {
@Bean @Bean
public TunnelClient tunnelClient() { public TunnelClient tunnelClient(@Value("${server.port}") int serverPort) {
String url = "http://localhost:" + this.httpServerPort + "/httptunnel"; String url = "http://localhost:" + serverPort + "/httptunnel";
TunnelConnection connection = new HttpTunnelConnection(url, TunnelConnection connection = new HttpTunnelConnection(url,
new SimpleClientHttpRequestFactory()); new SimpleClientHttpRequestFactory());
return new TunnelClient(this.clientPort, connection); return new TestTunnelClient(0, connection);
} }
@Bean static class TestTunnelClient extends TunnelClient {
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet(); 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();
} }
} }

@ -31,7 +31,6 @@ import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.springframework.util.SocketUtils;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PingMessage; import org.springframework.web.socket.PingMessage;
@ -56,14 +55,14 @@ public class LiveReloadServerTests {
private static final String HANDSHAKE = "{command: 'hello', " private static final String HANDSHAKE = "{command: 'hello', "
+ "protocols: ['http://livereload.com/protocols/official-7']}"; + "protocols: ['http://livereload.com/protocols/official-7']}";
private int port = SocketUtils.findAvailableTcpPort(); private int port;
private MonitoredLiveReloadServer server; private MonitoredLiveReloadServer server;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
this.server = new MonitoredLiveReloadServer(this.port); this.server = new MonitoredLiveReloadServer(0);
this.server.start(); this.port = this.server.start();
} }
@After @After

@ -26,7 +26,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
@ -39,13 +38,11 @@ import static org.mockito.Mockito.mock;
*/ */
public class LocalDebugPortAvailableConditionTests { public class LocalDebugPortAvailableConditionTests {
private int port = SocketUtils.findAvailableTcpPort();
private LocalDebugPortAvailableCondition condition = new LocalDebugPortAvailableCondition(); private LocalDebugPortAvailableCondition condition = new LocalDebugPortAvailableCondition();
@Test @Test
public void portAvailable() throws Exception { public void portAvailable() throws Exception {
ConditionOutcome outcome = getOutcome(); ConditionOutcome outcome = getOutcome(0);
assertThat(outcome.isMatch()).isTrue(); assertThat(outcome.isMatch()).isTrue();
assertThat(outcome.getMessage()) assertThat(outcome.getMessage())
.isEqualTo("Local Debug Port Condition found local debug port"); .isEqualTo("Local Debug Port Condition found local debug port");
@ -53,19 +50,19 @@ public class LocalDebugPortAvailableConditionTests {
@Test @Test
public void portInUse() throws Exception { public void portInUse() throws Exception {
final ServerSocket serverSocket = ServerSocketFactory.getDefault() ServerSocket serverSocket = ServerSocketFactory.getDefault()
.createServerSocket(this.port); .createServerSocket(0);
ConditionOutcome outcome = getOutcome(); ConditionOutcome outcome = getOutcome(serverSocket.getLocalPort());
serverSocket.close(); serverSocket.close();
assertThat(outcome.isMatch()).isFalse(); assertThat(outcome.isMatch()).isFalse();
assertThat(outcome.getMessage()) assertThat(outcome.getMessage())
.isEqualTo("Local Debug Port Condition did not find local debug port"); .isEqualTo("Local Debug Port Condition did not find local debug port");
} }
private ConditionOutcome getOutcome() { private ConditionOutcome getOutcome(int port) {
MockEnvironment environment = new MockEnvironment(); MockEnvironment environment = new MockEnvironment();
TestPropertyValues.of( TestPropertyValues.of("spring.devtools.remote.debug.local-port:" + port)
"spring.devtools.remote.debug.local-port:" + this.port).applyTo(environment); .applyTo(environment);
ConditionContext context = mock(ConditionContext.class); ConditionContext context = mock(ConditionContext.class);
given(context.getEnvironment()).willReturn(environment); given(context.getEnvironment()).willReturn(environment);
ConditionOutcome outcome = this.condition.getMatchOutcome(context, null); ConditionOutcome outcome = this.condition.getMatchOutcome(context, null);

@ -43,11 +43,11 @@ import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
@ -73,13 +73,16 @@ public class RemoteClientConfigurationTests {
private AnnotationConfigServletWebServerApplicationContext context; private AnnotationConfigServletWebServerApplicationContext context;
private static int remotePort = SocketUtils.findAvailableTcpPort(); private AnnotationConfigApplicationContext clientContext;
@After @After
public void cleanup() { public void cleanup() {
if (this.context != null) { if (this.context != null) {
this.context.close(); this.context.close();
} }
if (this.clientContext != null) {
this.clientContext.close();
}
} }
@Test @Test
@ -114,12 +117,12 @@ public class RemoteClientConfigurationTests {
configure(); configure();
Set<ChangedFiles> changeSet = new HashSet<>(); Set<ChangedFiles> changeSet = new HashSet<>();
ClassPathChangedEvent event = new ClassPathChangedEvent(this, changeSet, false); ClassPathChangedEvent event = new ClassPathChangedEvent(this, changeSet, false);
this.context.publishEvent(event); this.clientContext.publishEvent(event);
LiveReloadConfiguration configuration = this.context LiveReloadConfiguration configuration = this.clientContext
.getBean(LiveReloadConfiguration.class); .getBean(LiveReloadConfiguration.class);
configuration.getExecutor().shutdown(); configuration.getExecutor().shutdown();
configuration.getExecutor().awaitTermination(2, TimeUnit.SECONDS); configuration.getExecutor().awaitTermination(2, TimeUnit.SECONDS);
LiveReloadServer server = this.context.getBean(LiveReloadServer.class); LiveReloadServer server = this.clientContext.getBean(LiveReloadServer.class);
verify(server).triggerReload(); verify(server).triggerReload();
} }
@ -150,17 +153,24 @@ public class RemoteClientConfigurationTests {
private void configure(String remoteUrl, boolean setSecret, String... pairs) { private void configure(String remoteUrl, boolean setSecret, String... pairs) {
this.context = new AnnotationConfigServletWebServerApplicationContext(); this.context = new AnnotationConfigServletWebServerApplicationContext();
new RestartScopeInitializer().initialize(this.context); this.context.register(Config.class);
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);
if (setSecret) { if (setSecret) {
TestPropertyValues.of( TestPropertyValues.of("spring.devtools.remote.secret:secret")
"spring.devtools.remote.secret:secret").applyTo(this.context); .applyTo(this.context);
} }
this.context.refresh(); 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 @Configuration
@ -168,12 +178,7 @@ public class RemoteClientConfigurationTests {
@Bean @Bean
public TomcatServletWebServerFactory tomcat() { public TomcatServletWebServerFactory tomcat() {
return new TomcatServletWebServerFactory(remotePort); return new TomcatServletWebServerFactory(0);
}
@Bean
public LiveReloadServer liveReloadServer() {
return mock(LiveReloadServer.class);
} }
@Bean @Bean
@ -191,4 +196,14 @@ public class RemoteClientConfigurationTests {
} }
@Configuration
static class ClientConfig {
@Bean
public LiveReloadServer liveReloadServer() {
return mock(LiveReloadServer.class);
}
}
} }

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.devtools.tunnel.client.HttpTunnelConnection.TunnelChannel;
import org.springframework.boot.test.rule.OutputCapture; import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
@ -59,8 +58,6 @@ public class HttpTunnelConnectionTests {
@Rule @Rule
public OutputCapture outputCapture = new OutputCapture(); public OutputCapture outputCapture = new OutputCapture();
private int port = SocketUtils.findAvailableTcpPort();
private String url; private String url;
private ByteArrayOutputStream incomingData; private ByteArrayOutputStream incomingData;
@ -75,7 +72,7 @@ public class HttpTunnelConnectionTests {
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
this.url = "http://localhost:" + this.port; this.url = "http://localhost:12345";
this.incomingData = new ByteArrayOutputStream(); this.incomingData = new ByteArrayOutputStream();
this.incomingChannel = Channels.newChannel(this.incomingData); this.incomingChannel = Channels.newChannel(this.incomingData);
} }
@ -164,8 +161,7 @@ public class HttpTunnelConnectionTests {
TunnelChannel tunnel = openTunnel(true); TunnelChannel tunnel = openTunnel(true);
assertThat(tunnel.isOpen()).isFalse(); assertThat(tunnel.isOpen()).isFalse();
this.outputCapture.expect(containsString( this.outputCapture.expect(containsString(
"Failed to connect to remote application at http://localhost:" "Failed to connect to remote application at http://localhost:12345"));
+ this.port));
} }
private void write(TunnelChannel channel, String string) throws IOException { private void write(TunnelChannel channel, String string) throws IOException {

@ -29,8 +29,6 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -46,15 +44,13 @@ public class TunnelClientTests {
@Rule @Rule
public ExpectedException thrown = ExpectedException.none(); public ExpectedException thrown = ExpectedException.none();
private int listenPort = SocketUtils.findAvailableTcpPort();
private MockTunnelConnection tunnelConnection = new MockTunnelConnection(); private MockTunnelConnection tunnelConnection = new MockTunnelConnection();
@Test @Test
public void listenPortMustBePositive() throws Exception { public void listenPortMustNotBeNegative() throws Exception {
this.thrown.expect(IllegalArgumentException.class); this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("ListenPort must be positive"); this.thrown.expectMessage("ListenPort must be greater than or equal to 0");
new TunnelClient(0, this.tunnelConnection); new TunnelClient(-5, this.tunnelConnection);
} }
@Test @Test
@ -66,10 +62,9 @@ public class TunnelClientTests {
@Test @Test
public void typicalTraffic() throws Exception { public void typicalTraffic() throws Exception {
TunnelClient client = new TunnelClient(this.listenPort, this.tunnelConnection); TunnelClient client = new TunnelClient(0, this.tunnelConnection);
client.start(); int port = client.start();
SocketChannel channel = SocketChannel SocketChannel channel = SocketChannel.open(new InetSocketAddress(port));
.open(new InetSocketAddress(this.listenPort));
channel.write(ByteBuffer.wrap("hello".getBytes())); channel.write(ByteBuffer.wrap("hello".getBytes()));
ByteBuffer buffer = ByteBuffer.allocate(5); ByteBuffer buffer = ByteBuffer.allocate(5);
channel.read(buffer); channel.read(buffer);
@ -80,10 +75,9 @@ public class TunnelClientTests {
@Test @Test
public void socketChannelClosedTriggersTunnelClose() throws Exception { public void socketChannelClosedTriggersTunnelClose() throws Exception {
TunnelClient client = new TunnelClient(this.listenPort, this.tunnelConnection); TunnelClient client = new TunnelClient(0, this.tunnelConnection);
client.start(); int port = client.start();
SocketChannel channel = SocketChannel SocketChannel channel = SocketChannel.open(new InetSocketAddress(port));
.open(new InetSocketAddress(this.listenPort));
Thread.sleep(200); Thread.sleep(200);
channel.close(); channel.close();
client.getServerThread().stopAcceptingConnections(); client.getServerThread().stopAcceptingConnections();
@ -94,10 +88,9 @@ public class TunnelClientTests {
@Test @Test
public void stopTriggersTunnelClose() throws Exception { public void stopTriggersTunnelClose() throws Exception {
TunnelClient client = new TunnelClient(this.listenPort, this.tunnelConnection); TunnelClient client = new TunnelClient(0, this.tunnelConnection);
client.start(); int port = client.start();
SocketChannel channel = SocketChannel SocketChannel channel = SocketChannel.open(new InetSocketAddress(port));
.open(new InetSocketAddress(this.listenPort));
Thread.sleep(200); Thread.sleep(200);
client.stop(); client.stop();
assertThat(this.tunnelConnection.getOpenedTimes()).isEqualTo(1); assertThat(this.tunnelConnection.getOpenedTimes()).isEqualTo(1);
@ -107,12 +100,11 @@ public class TunnelClientTests {
@Test @Test
public void addListener() throws Exception { 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); TunnelClientListener listener = mock(TunnelClientListener.class);
client.addListener(listener); client.addListener(listener);
client.start(); int port = client.start();
SocketChannel channel = SocketChannel SocketChannel channel = SocketChannel.open(new InetSocketAddress(port));
.open(new InetSocketAddress(this.listenPort));
Thread.sleep(200); Thread.sleep(200);
channel.close(); channel.close();
client.getServerThread().stopAcceptingConnections(); client.getServerThread().stopAcceptingConnections();

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Before;
import org.junit.Test; import org.junit.Test;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -41,18 +39,14 @@ public class SocketTargetServerConnectionTests {
private static final int DEFAULT_TIMEOUT = 1000; private static final int DEFAULT_TIMEOUT = 1000;
private int port;
private MockServer server; private MockServer server;
private SocketTargetServerConnection connection; private SocketTargetServerConnection connection;
@Before @Before
public void setup() throws IOException { public void setup() throws IOException {
this.port = SocketUtils.findAvailableTcpPort(); this.server = new MockServer();
this.server = new MockServer(this.port); this.connection = new SocketTargetServerConnection(() -> this.server.getPort());
StaticPortProvider portProvider = new StaticPortProvider(this.port);
this.connection = new SocketTargetServerConnection(portProvider);
} }
@Test @Test
@ -107,9 +101,13 @@ public class SocketTargetServerConnectionTests {
private ServerThread thread; private ServerThread thread;
MockServer(int port) throws IOException { MockServer() throws IOException {
this.serverSocket = ServerSocketChannel.open(); 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) { public void delay(int delay) {

@ -17,13 +17,14 @@
package org.springframework.boot.devtools.tests; package org.springframework.boot.devtools.tests;
import java.io.File; import java.io.File;
import java.io.FileReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.boot.devtools.RemoteSpringApplication; import org.springframework.boot.devtools.RemoteSpringApplication;
import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm; import org.springframework.boot.devtools.tests.JvmLauncher.LaunchedJvm;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.FileSystemUtils; import org.springframework.util.FileSystemUtils;
import org.springframework.util.SocketUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -37,14 +38,15 @@ abstract class RemoteApplicationLauncher implements ApplicationLauncher {
@Override @Override
public LaunchedApplication launchApplication(JvmLauncher javaLauncher) public LaunchedApplication launchApplication(JvmLauncher javaLauncher)
throws Exception { throws Exception {
int port = SocketUtils.findAvailableTcpPort();
LaunchedJvm applicationJvm = javaLauncher.launch("app", LaunchedJvm applicationJvm = javaLauncher.launch("app",
createApplicationClassPath(), "com.example.DevToolsTestApplication", 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( LaunchedJvm remoteSpringApplicationJvm = javaLauncher.launch(
"remote-spring-application", createRemoteSpringApplicationClassPath(), "remote-spring-application", createRemoteSpringApplicationClassPath(),
RemoteSpringApplication.class.getName(), 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"), return new LaunchedApplication(new File("target/remote"),
applicationJvm.getStandardOut(), applicationJvm.getProcess(), applicationJvm.getStandardOut(), applicationJvm.getProcess(),
remoteSpringApplicationJvm.getProcess()); remoteSpringApplicationJvm.getProcess());
@ -66,4 +68,40 @@ abstract class RemoteApplicationLauncher implements ApplicationLauncher {
return StringUtils.collectionToDelimitedString(entries, File.pathSeparator); 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);
}
}
} }

@ -23,7 +23,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.util.SocketUtils;
/** /**
* Sample Application to show Tomcat running two connectors * Sample Application to show Tomcat running two connectors
@ -34,11 +33,6 @@ import org.springframework.util.SocketUtils;
@SpringBootApplication @SpringBootApplication
public class SampleTomcatTwoConnectorsApplication { public class SampleTomcatTwoConnectorsApplication {
@Bean
public Integer port() {
return SocketUtils.findAvailableTcpPort();
}
@Bean @Bean
public ServletWebServerFactory servletContainer() { public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
@ -48,7 +42,7 @@ public class SampleTomcatTwoConnectorsApplication {
private Connector createStandardConnector() { private Connector createStandardConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setPort(port()); connector.setPort(0);
return connector; return connector;
} }

@ -22,19 +22,26 @@ import java.net.HttpURLConnection;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import org.apache.catalina.Service;
import org.apache.catalina.connector.Connector;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import sample.tomcat.multiconnector.SampleTomcatTwoConnectorsApplicationTests.Ports;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; 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.boot.web.server.LocalServerPort;
import org.springframework.context.ApplicationContext; 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.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory;
@ -53,10 +60,11 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext @DirtiesContext
@Import(Ports.class)
public class SampleTomcatTwoConnectorsApplicationTests { public class SampleTomcatTwoConnectorsApplicationTests {
@LocalServerPort @LocalServerPort
private String port; private int port;
@Autowired @Autowired
private ApplicationContext context; private ApplicationContext context;
@ -97,27 +105,19 @@ public class SampleTomcatTwoConnectorsApplicationTests {
public void testHello() throws Exception { public void testHello() throws Exception {
RestTemplate template = new RestTemplate(); RestTemplate template = new RestTemplate();
final MySimpleClientHttpRequestFactory factory = new MySimpleClientHttpRequestFactory( final MySimpleClientHttpRequestFactory factory = new MySimpleClientHttpRequestFactory(
new HostnameVerifier() { (hostname, session) -> true);
@Override
public boolean verify(final String hostname,
final SSLSession session) {
return true; // these guys are alright by me...
}
});
template.setRequestFactory(factory); template.setRequestFactory(factory);
Ports ports = this.context.getBean(Ports.class);
assertThat(ports.getHttpsPort()).isEqualTo(this.port);
assertThat(ports.getHttpPort()).isNotEqualTo(this.port);
ResponseEntity<String> entity = template.getForEntity( ResponseEntity<String> entity = template.getForEntity(
"http://localhost:" + this.context.getBean("port") + "/hello", "http://localhost:" + ports.getHttpPort() + "/hello", String.class);
String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(entity.getBody()).isEqualTo("hello"); assertThat(entity.getBody()).isEqualTo("hello");
ResponseEntity<String> httpsEntity = template ResponseEntity<String> httpsEntity = template
.getForEntity("https://localhost:" + this.port + "/hello", String.class); .getForEntity("https://localhost:" + this.port + "/hello", String.class);
assertThat(httpsEntity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(httpsEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(httpsEntity.getBody()).isEqualTo("hello"); assertThat(httpsEntity.getBody()).isEqualTo("hello");
} }
/** /**
@ -141,4 +141,35 @@ public class SampleTomcatTwoConnectorsApplicationTests {
} }
} }
@TestConfiguration
static class Ports implements ApplicationListener<WebServerInitializedEvent> {
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;
}
}
} }

@ -37,13 +37,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; 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.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner; 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.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient; import org.springframework.web.socket.client.standard.StandardWebSocketClient;
@ -51,20 +51,21 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = { SampleJettyWebSocketsApplication.class, @SpringBootTest(classes = { SampleJettyWebSocketsApplication.class,
CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT) CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext @DirtiesContext
public class CustomContainerWebSocketsApplicationTests { public class CustomContainerWebSocketsApplicationTests {
private static Log logger = LogFactory private static Log logger = LogFactory
.getLog(CustomContainerWebSocketsApplicationTests.class); .getLog(CustomContainerWebSocketsApplicationTests.class);
private static int PORT = SocketUtils.findAvailableTcpPort(); @LocalServerPort
private int port;
@Test @Test
public void echoEndpoint() throws Exception { public void echoEndpoint() throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + PORT .properties("websocket.uri:ws://localhost:" + this.port
+ "/ws/echo/websocket") + "/ws/echo/websocket")
.run("--spring.main.web_environment=false"); .run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
@ -80,8 +81,8 @@ public class CustomContainerWebSocketsApplicationTests {
public void reverseEndpoint() throws Exception { public void reverseEndpoint() throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties( .properties("websocket.uri:ws://localhost:" + this.port
"websocket.uri:ws://localhost:" + PORT + "/ws/reverse") + "/ws/reverse")
.run("--spring.main.web_environment=false"); .run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context AtomicReference<String> messagePayloadReference = context
@ -96,7 +97,7 @@ public class CustomContainerWebSocketsApplicationTests {
@Bean @Bean
public ServletWebServerFactory webServerFactory() { public ServletWebServerFactory webServerFactory() {
return new JettyServletWebServerFactory("/ws", PORT); return new JettyServletWebServerFactory("/ws", 0);
} }
} }

@ -37,13 +37,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; 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.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner; 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.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient; import org.springframework.web.socket.client.standard.StandardWebSocketClient;
@ -51,20 +51,21 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = { SampleTomcatWebSocketApplication.class, @SpringBootTest(classes = { SampleTomcatWebSocketApplication.class,
CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT) CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext @DirtiesContext
public class CustomContainerWebSocketsApplicationTests { public class CustomContainerWebSocketsApplicationTests {
private static Log logger = LogFactory private static Log logger = LogFactory
.getLog(CustomContainerWebSocketsApplicationTests.class); .getLog(CustomContainerWebSocketsApplicationTests.class);
private static int PORT = SocketUtils.findAvailableTcpPort(); @LocalServerPort
private int port;
@Test @Test
public void echoEndpoint() throws Exception { public void echoEndpoint() throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + PORT .properties("websocket.uri:ws://localhost:" + this.port
+ "/ws/echo/websocket") + "/ws/echo/websocket")
.run("--spring.main.web_environment=false"); .run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
@ -80,8 +81,8 @@ public class CustomContainerWebSocketsApplicationTests {
public void reverseEndpoint() throws Exception { public void reverseEndpoint() throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties( .properties("websocket.uri:ws://localhost:" + this.port
"websocket.uri:ws://localhost:" + PORT + "/ws/reverse") + "/ws/reverse")
.run("--spring.main.web_environment=false"); .run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context AtomicReference<String> messagePayloadReference = context
@ -96,7 +97,7 @@ public class CustomContainerWebSocketsApplicationTests {
@Bean @Bean
public ServletWebServerFactory webServerFactory() { public ServletWebServerFactory webServerFactory() {
return new TomcatServletWebServerFactory("/ws", PORT); return new TomcatServletWebServerFactory("/ws", 0);
} }
} }

@ -37,13 +37,13 @@ import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; 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.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner; 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.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient; import org.springframework.web.socket.client.standard.StandardWebSocketClient;
@ -51,20 +51,21 @@ import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest(classes = { SampleUndertowWebSocketsApplication.class, @SpringBootTest(classes = { SampleUndertowWebSocketsApplication.class,
CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.DEFINED_PORT) CustomContainerConfiguration.class }, webEnvironment = WebEnvironment.RANDOM_PORT)
@DirtiesContext @DirtiesContext
public class CustomContainerWebSocketsApplicationTests { public class CustomContainerWebSocketsApplicationTests {
private static Log logger = LogFactory private static Log logger = LogFactory
.getLog(CustomContainerWebSocketsApplicationTests.class); .getLog(CustomContainerWebSocketsApplicationTests.class);
private static int PORT = SocketUtils.findAvailableTcpPort(); @LocalServerPort
private int port;
@Test @Test
public void echoEndpoint() throws Exception { public void echoEndpoint() throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + PORT .properties("websocket.uri:ws://localhost:" + this.port
+ "/ws/echo/websocket") + "/ws/echo/websocket")
.run("--spring.main.web_environment=false"); .run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
@ -80,8 +81,8 @@ public class CustomContainerWebSocketsApplicationTests {
public void reverseEndpoint() throws Exception { public void reverseEndpoint() throws Exception {
ConfigurableApplicationContext context = new SpringApplicationBuilder( ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class) ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties( .properties("websocket.uri:ws://localhost:" + this.port
"websocket.uri:ws://localhost:" + PORT + "/ws/reverse") + "/ws/reverse")
.run("--spring.main.web_environment=false"); .run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount(); long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context AtomicReference<String> messagePayloadReference = context
@ -96,7 +97,7 @@ public class CustomContainerWebSocketsApplicationTests {
@Bean @Bean
public ServletWebServerFactory webServerFactory() { public ServletWebServerFactory webServerFactory() {
return new UndertowServletWebServerFactory("/ws", PORT); return new UndertowServletWebServerFactory("/ws", 0);
} }
} }

@ -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.AbstractServletWebServerFactory;
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.SocketUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -92,7 +91,7 @@ public class TomcatServletWebServerFactoryTests
public void tomcatEngineNames() throws Exception { public void tomcatEngineNames() throws Exception {
TomcatServletWebServerFactory factory = getFactory(); TomcatServletWebServerFactory factory = getFactory();
this.webServer = factory.getWebServer(); this.webServer = factory.getWebServer();
factory.setPort(SocketUtils.findAvailableTcpPort(40000)); factory.setPort(0);
TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer(); TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer();
// Make sure that the names are different // Make sure that the names are different
String firstName = ((TomcatWebServer) this.webServer).getTomcat().getEngine() String firstName = ((TomcatWebServer) this.webServer).getTomcat().getEngine()

Loading…
Cancel
Save