Use server.port=0 for scanning
This leverages existing capabilities of teh JDK and the OS to grab a port at random and not have it stolen by another process. It's very hard to avoid that race condition in pure Java code, so why bother? User can set port<0 to disable autoStart of connectors (e.g. to start a web application context but not have it listen on any port). In that case the actual socket port will be set to 0 (and therefore if it ever starts up the local port will be random).
This commit is contained in:
@@ -35,7 +35,6 @@ import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomize
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -54,8 +53,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||
|
||||
private Integer sessionTimeout;
|
||||
|
||||
private boolean scan = false;
|
||||
|
||||
@NotNull
|
||||
private String contextPath = "";
|
||||
|
||||
@@ -81,14 +78,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public boolean getScan() {
|
||||
return this.scan;
|
||||
}
|
||||
|
||||
public void setScan(boolean scan) {
|
||||
this.scan = scan;
|
||||
}
|
||||
|
||||
public InetAddress getAddress() {
|
||||
return this.address;
|
||||
}
|
||||
@@ -112,9 +101,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||
@Override
|
||||
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
|
||||
Integer port = getPort();
|
||||
if (this.scan) {
|
||||
port = scanForPort(port);
|
||||
}
|
||||
if (port != null) {
|
||||
factory.setPort(port);
|
||||
}
|
||||
@@ -132,26 +118,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
||||
}
|
||||
}
|
||||
|
||||
private Integer scanForPort(Integer port) {
|
||||
boolean found = false;
|
||||
int delta = 1;
|
||||
port = port == null ? 8080 : port;
|
||||
while (!found) {
|
||||
try {
|
||||
port = SocketUtils.findAvailableTcpPort(port, port + delta);
|
||||
found = true;
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
if (delta > 65536) {
|
||||
throw e;
|
||||
}
|
||||
delta = delta > 5 ? delta > 100 ? delta * 4 : delta * 3 : delta * 2;
|
||||
}
|
||||
port = port + delta;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
public static class Tomcat {
|
||||
|
||||
private String accessLogPattern;
|
||||
|
||||
@@ -24,12 +24,9 @@ import java.util.Map;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.MockEmbeddedServletContainerFactory;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for {@link ServerProperties}.
|
||||
@@ -71,24 +68,6 @@ public class ServerPropertiesTests {
|
||||
.getProtocolHeader());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPortScan() throws Exception {
|
||||
this.properties.setScan(true);
|
||||
this.properties.setPort(1000);
|
||||
ConfigurableEmbeddedServletContainerFactory factory = new MockEmbeddedServletContainerFactory();
|
||||
this.properties.customize(factory);
|
||||
assertTrue(factory.getPort() > 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPortScanFromHigher() throws Exception {
|
||||
this.properties.setScan(true);
|
||||
this.properties.setPort(5678);
|
||||
ConfigurableEmbeddedServletContainerFactory factory = new MockEmbeddedServletContainerFactory();
|
||||
this.properties.customize(factory);
|
||||
assertTrue(factory.getPort() < 6000);
|
||||
}
|
||||
|
||||
// FIXME test customize
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<execution>
|
||||
<id>aggregate-javadocs</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
<goal>aggregate-jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeDependencySources>true</includeDependencySources>
|
||||
|
||||
@@ -82,7 +82,6 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
|
||||
* @param port the port number for the embedded servlet container
|
||||
*/
|
||||
public AbstractEmbeddedServletContainerFactory(int port) {
|
||||
checkPort(port);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
@@ -94,7 +93,6 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
|
||||
*/
|
||||
public AbstractEmbeddedServletContainerFactory(String contextPath, int port) {
|
||||
checkContextPath(contextPath);
|
||||
checkPort(port);
|
||||
this.contextPath = contextPath;
|
||||
this.port = port;
|
||||
}
|
||||
@@ -129,16 +127,9 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
|
||||
|
||||
@Override
|
||||
public void setPort(int port) {
|
||||
checkPort(port);
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
private void checkPort(int port) {
|
||||
if (port < 0 || port > 65535) {
|
||||
throw new IllegalArgumentException("Port must be between 1 and 65535");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port that the embedded servlet container should listen on.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.boot.context.embedded.jetty;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||
@@ -28,10 +30,13 @@ import org.springframework.util.Assert;
|
||||
* {@link JettyEmbeddedServletContainerFactory} and not directly.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @see JettyEmbeddedServletContainerFactory
|
||||
*/
|
||||
public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||
|
||||
private final Log logger = LogFactory.getLog(JettyEmbeddedServletContainer.class);
|
||||
|
||||
private final Server server;
|
||||
private boolean autoStart;
|
||||
|
||||
@@ -81,6 +86,7 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||
Connector[] connectors = this.server.getConnectors();
|
||||
for (Connector connector : connectors) {
|
||||
connector.start();
|
||||
this.logger.info("Jetty started on port: " + connector.getLocalPort());
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
|
||||
@@ -97,7 +97,8 @@ public class JettyEmbeddedServletContainerFactory extends
|
||||
public EmbeddedServletContainer getEmbeddedServletContainer(
|
||||
ServletContextInitializer... initializers) {
|
||||
WebAppContext context = new WebAppContext();
|
||||
Server server = new Server(new InetSocketAddress(getAddress(), getPort()));
|
||||
int port = getPort() >= 0 ? getPort() : 0;
|
||||
Server server = new Server(new InetSocketAddress(getAddress(), port));
|
||||
|
||||
if (this.resourceLoader != null) {
|
||||
context.setClassLoader(this.resourceLoader.getClassLoader());
|
||||
@@ -123,7 +124,7 @@ public class JettyEmbeddedServletContainerFactory extends
|
||||
postProcessWebAppContext(context);
|
||||
|
||||
server.setHandler(context);
|
||||
this.logger.info("Server initialized with port: " + getPort());
|
||||
this.logger.info("Server initialized with port: " + port);
|
||||
return getJettyEmbeddedServletContainer(server);
|
||||
}
|
||||
|
||||
@@ -241,7 +242,7 @@ public class JettyEmbeddedServletContainerFactory extends
|
||||
* @return a new {@link JettyEmbeddedServletContainer} instance
|
||||
*/
|
||||
protected JettyEmbeddedServletContainer getJettyEmbeddedServletContainer(Server server) {
|
||||
return new JettyEmbeddedServletContainer(server, getPort() > 0);
|
||||
return new JettyEmbeddedServletContainer(server, getPort() >= 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -104,6 +104,7 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||
if (connector != null && this.autoStart) {
|
||||
try {
|
||||
connector.getProtocolHandler().start();
|
||||
this.logger.info("Tomcat started on port: " + connector.getLocalPort());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
this.logger.error("Cannot start connector: ", ex);
|
||||
|
||||
@@ -193,12 +193,8 @@ public class TomcatEmbeddedServletContainerFactory extends
|
||||
|
||||
// Needs to be protected so it can be used by subclasses
|
||||
protected void customizeConnector(Connector connector) {
|
||||
if (getPort() > 0) {
|
||||
connector.setPort(getPort());
|
||||
}
|
||||
else {
|
||||
connector.setPort(8080);
|
||||
}
|
||||
int port = getPort() >= 0 ? getPort() : 0;
|
||||
connector.setPort(port);
|
||||
if (connector.getProtocolHandler() instanceof AbstractProtocol) {
|
||||
if (getAddress() != null) {
|
||||
((AbstractProtocol) connector.getProtocolHandler())
|
||||
@@ -259,7 +255,7 @@ public class TomcatEmbeddedServletContainerFactory extends
|
||||
*/
|
||||
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
|
||||
Tomcat tomcat) {
|
||||
return new TomcatEmbeddedServletContainer(tomcat, getPort() > 0);
|
||||
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
|
||||
}
|
||||
|
||||
private File createTempDir(String prefix) {
|
||||
|
||||
Reference in New Issue
Block a user