Skip to content

Commit

Permalink
Minimise our usage of SocketUtils.findAvailableTcpPort
Browse files Browse the repository at this point in the history
Closes gh-9382
  • Loading branch information
wilkinsona committed Jun 9, 2017
1 parent 1086851 commit f7e9ec5
Show file tree
Hide file tree
Showing 26 changed files with 434 additions and 278 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -67,19 +65,18 @@
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;
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;
Expand All @@ -88,7 +85,6 @@
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;
Expand Down Expand Up @@ -134,7 +130,6 @@ public void setUp() {
@After
public void cleanUp() throws Exception {
this.applicationContext.close();
assertAllClosed();
}

@Test
Expand All @@ -146,8 +141,6 @@ public void onSamePort() throws Exception {
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"))
Expand Down Expand Up @@ -274,25 +267,6 @@ public void onDifferentPortInWebServer() throws Exception {
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)
Expand Down Expand Up @@ -340,20 +314,22 @@ public void specificPortsViaProperties() throws Exception {
}

@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<ContextRefreshedEvent>) (event) -> {
if (event.getApplicationContext().getParent() != null) {
throw new RuntimeException();
}
});
this.thrown.expect(RuntimeException.class);
this.applicationContext.refresh();
}

@Test
Expand Down Expand Up @@ -671,13 +647,6 @@ private void endpointEnabledOverride(String name, Class<? extends MvcEndpoint> t
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);
Expand Down Expand Up @@ -733,9 +702,9 @@ public boolean hasHeader(String url, int port, String header) throws Exception {

private static class Ports {

final int server = SocketUtils.findAvailableTcpPort();
int server = 0;

final int management = SocketUtils.findAvailableTcpPort();
int management = 0;

}

Expand All @@ -758,6 +727,19 @@ public TestController 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
Expand Down Expand Up @@ -886,30 +868,6 @@ public Class<? extends Endpoint> getEndpointType() {

}

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
extends TomcatServletWebServerFactory {

Expand Down
Expand Up @@ -57,7 +57,6 @@
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;
Expand Down Expand Up @@ -347,9 +346,7 @@ static class TomcatConfiguration {

@Bean
public TomcatServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(SocketUtils.findAvailableTcpPort(40000));
return factory;
return new TomcatServletWebServerFactory(0);
}

}
Expand Down
Expand Up @@ -25,7 +25,6 @@
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;

Expand Down Expand Up @@ -55,9 +54,7 @@ static class Config {

@Bean
public TomcatServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort(SocketUtils.findAvailableTcpPort(40000));
return factory;
return new TomcatServletWebServerFactory(0);
}

@Bean
Expand Down
Expand Up @@ -28,7 +28,6 @@

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;

Expand All @@ -40,12 +39,10 @@
*/
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() {
Expand Down Expand Up @@ -84,15 +81,15 @@ public void setTimerMetric() throws Exception {

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

@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");
Expand Down Expand Up @@ -129,6 +126,10 @@ private static final class DummyStatsDServer implements Runnable {
new Thread(this).start();
}

int getPort() {
return this.server.getLocalPort();
}

public void stop() {
this.server.close();
}
Expand Down
Expand Up @@ -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;

Expand All @@ -26,22 +30,28 @@
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;
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;
Expand Down Expand Up @@ -105,12 +115,12 @@ public void proxyHostWithoutPort() {

@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<String, String> source = new HashMap<>();
source.put("a", "alpha");
Expand Down Expand Up @@ -182,4 +192,53 @@ Gson getGson() {

}

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

}

}

0 comments on commit f7e9ec5

Please sign in to comment.