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.TomcatContextCustomizer;
|
||||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.util.SocketUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,8 +53,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
|||||||
|
|
||||||
private Integer sessionTimeout;
|
private Integer sessionTimeout;
|
||||||
|
|
||||||
private boolean scan = false;
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String contextPath = "";
|
private String contextPath = "";
|
||||||
|
|
||||||
@@ -81,14 +78,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
|||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getScan() {
|
|
||||||
return this.scan;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScan(boolean scan) {
|
|
||||||
this.scan = scan;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InetAddress getAddress() {
|
public InetAddress getAddress() {
|
||||||
return this.address;
|
return this.address;
|
||||||
}
|
}
|
||||||
@@ -112,9 +101,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
|
|||||||
@Override
|
@Override
|
||||||
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
|
public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
|
||||||
Integer port = getPort();
|
Integer port = getPort();
|
||||||
if (this.scan) {
|
|
||||||
port = scanForPort(port);
|
|
||||||
}
|
|
||||||
if (port != null) {
|
if (port != null) {
|
||||||
factory.setPort(port);
|
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 {
|
public static class Tomcat {
|
||||||
|
|
||||||
private String accessLogPattern;
|
private String accessLogPattern;
|
||||||
|
|||||||
@@ -24,12 +24,9 @@ import java.util.Map;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.MutablePropertyValues;
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
import org.springframework.boot.bind.RelaxedDataBinder;
|
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.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link ServerProperties}.
|
* Tests for {@link ServerProperties}.
|
||||||
@@ -71,24 +68,6 @@ public class ServerPropertiesTests {
|
|||||||
.getProtocolHeader());
|
.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
|
// FIXME test customize
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
<execution>
|
<execution>
|
||||||
<id>aggregate-javadocs</id>
|
<id>aggregate-javadocs</id>
|
||||||
<goals>
|
<goals>
|
||||||
<goal>jar</goal>
|
<goal>aggregate-jar</goal>
|
||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<includeDependencySources>true</includeDependencySources>
|
<includeDependencySources>true</includeDependencySources>
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
|
|||||||
* @param port the port number for the embedded servlet container
|
* @param port the port number for the embedded servlet container
|
||||||
*/
|
*/
|
||||||
public AbstractEmbeddedServletContainerFactory(int port) {
|
public AbstractEmbeddedServletContainerFactory(int port) {
|
||||||
checkPort(port);
|
|
||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +93,6 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
|
|||||||
*/
|
*/
|
||||||
public AbstractEmbeddedServletContainerFactory(String contextPath, int port) {
|
public AbstractEmbeddedServletContainerFactory(String contextPath, int port) {
|
||||||
checkContextPath(contextPath);
|
checkContextPath(contextPath);
|
||||||
checkPort(port);
|
|
||||||
this.contextPath = contextPath;
|
this.contextPath = contextPath;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
}
|
}
|
||||||
@@ -129,16 +127,9 @@ public abstract class AbstractEmbeddedServletContainerFactory implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPort(int port) {
|
public void setPort(int port) {
|
||||||
checkPort(port);
|
|
||||||
this.port = 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.
|
* Returns the port that the embedded servlet container should listen on.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.boot.context.embedded.jetty;
|
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.Connector;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||||
@@ -28,10 +30,13 @@ import org.springframework.util.Assert;
|
|||||||
* {@link JettyEmbeddedServletContainerFactory} and not directly.
|
* {@link JettyEmbeddedServletContainerFactory} and not directly.
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
|
* @author Dave Syer
|
||||||
* @see JettyEmbeddedServletContainerFactory
|
* @see JettyEmbeddedServletContainerFactory
|
||||||
*/
|
*/
|
||||||
public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||||
|
|
||||||
|
private final Log logger = LogFactory.getLog(JettyEmbeddedServletContainer.class);
|
||||||
|
|
||||||
private final Server server;
|
private final Server server;
|
||||||
private boolean autoStart;
|
private boolean autoStart;
|
||||||
|
|
||||||
@@ -81,6 +86,7 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
|||||||
Connector[] connectors = this.server.getConnectors();
|
Connector[] connectors = this.server.getConnectors();
|
||||||
for (Connector connector : connectors) {
|
for (Connector connector : connectors) {
|
||||||
connector.start();
|
connector.start();
|
||||||
|
this.logger.info("Jetty started on port: " + connector.getLocalPort());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ public class JettyEmbeddedServletContainerFactory extends
|
|||||||
public EmbeddedServletContainer getEmbeddedServletContainer(
|
public EmbeddedServletContainer getEmbeddedServletContainer(
|
||||||
ServletContextInitializer... initializers) {
|
ServletContextInitializer... initializers) {
|
||||||
WebAppContext context = new WebAppContext();
|
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) {
|
if (this.resourceLoader != null) {
|
||||||
context.setClassLoader(this.resourceLoader.getClassLoader());
|
context.setClassLoader(this.resourceLoader.getClassLoader());
|
||||||
@@ -123,7 +124,7 @@ public class JettyEmbeddedServletContainerFactory extends
|
|||||||
postProcessWebAppContext(context);
|
postProcessWebAppContext(context);
|
||||||
|
|
||||||
server.setHandler(context);
|
server.setHandler(context);
|
||||||
this.logger.info("Server initialized with port: " + getPort());
|
this.logger.info("Server initialized with port: " + port);
|
||||||
return getJettyEmbeddedServletContainer(server);
|
return getJettyEmbeddedServletContainer(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +242,7 @@ public class JettyEmbeddedServletContainerFactory extends
|
|||||||
* @return a new {@link JettyEmbeddedServletContainer} instance
|
* @return a new {@link JettyEmbeddedServletContainer} instance
|
||||||
*/
|
*/
|
||||||
protected JettyEmbeddedServletContainer getJettyEmbeddedServletContainer(Server server) {
|
protected JettyEmbeddedServletContainer getJettyEmbeddedServletContainer(Server server) {
|
||||||
return new JettyEmbeddedServletContainer(server, getPort() > 0);
|
return new JettyEmbeddedServletContainer(server, getPort() >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
|||||||
if (connector != null && this.autoStart) {
|
if (connector != null && this.autoStart) {
|
||||||
try {
|
try {
|
||||||
connector.getProtocolHandler().start();
|
connector.getProtocolHandler().start();
|
||||||
|
this.logger.info("Tomcat started on port: " + connector.getLocalPort());
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
this.logger.error("Cannot start connector: ", 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
|
// Needs to be protected so it can be used by subclasses
|
||||||
protected void customizeConnector(Connector connector) {
|
protected void customizeConnector(Connector connector) {
|
||||||
if (getPort() > 0) {
|
int port = getPort() >= 0 ? getPort() : 0;
|
||||||
connector.setPort(getPort());
|
connector.setPort(port);
|
||||||
}
|
|
||||||
else {
|
|
||||||
connector.setPort(8080);
|
|
||||||
}
|
|
||||||
if (connector.getProtocolHandler() instanceof AbstractProtocol) {
|
if (connector.getProtocolHandler() instanceof AbstractProtocol) {
|
||||||
if (getAddress() != null) {
|
if (getAddress() != null) {
|
||||||
((AbstractProtocol) connector.getProtocolHandler())
|
((AbstractProtocol) connector.getProtocolHandler())
|
||||||
@@ -259,7 +255,7 @@ public class TomcatEmbeddedServletContainerFactory extends
|
|||||||
*/
|
*/
|
||||||
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
|
protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(
|
||||||
Tomcat tomcat) {
|
Tomcat tomcat) {
|
||||||
return new TomcatEmbeddedServletContainer(tomcat, getPort() > 0);
|
return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private File createTempDir(String prefix) {
|
private File createTempDir(String prefix) {
|
||||||
|
|||||||
Reference in New Issue
Block a user