Ensure embedded start can be called multiple times
Update all `EmbeddedServletContainer` implementations to ensure that the `start()` method can be called multiple times, with the second call being ignored. Fixes gh-8036
This commit is contained in:
parent
ef69ae6a89
commit
0af53b361f
|
@ -59,6 +59,8 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||||
|
|
||||||
private Connector[] connectors;
|
private Connector[] connectors;
|
||||||
|
|
||||||
|
private volatile boolean started;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link JettyEmbeddedServletContainer} instance.
|
* Create a new {@link JettyEmbeddedServletContainer} instance.
|
||||||
* @param server the underlying Jetty server
|
* @param server the underlying Jetty server
|
||||||
|
@ -111,37 +113,43 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws EmbeddedServletContainerException {
|
public void start() throws EmbeddedServletContainerException {
|
||||||
this.server.setConnectors(this.connectors);
|
synchronized (this.monitor) {
|
||||||
if (!this.autoStart) {
|
if (this.started) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.server.start();
|
|
||||||
for (Handler handler : this.server.getHandlers()) {
|
|
||||||
handleDeferredInitialize(handler);
|
|
||||||
}
|
}
|
||||||
Connector[] connectors = this.server.getConnectors();
|
this.server.setConnectors(this.connectors);
|
||||||
for (Connector connector : connectors) {
|
if (!this.autoStart) {
|
||||||
try {
|
return;
|
||||||
connector.start();
|
}
|
||||||
|
try {
|
||||||
|
this.server.start();
|
||||||
|
for (Handler handler : this.server.getHandlers()) {
|
||||||
|
handleDeferredInitialize(handler);
|
||||||
}
|
}
|
||||||
catch (BindException ex) {
|
Connector[] connectors = this.server.getConnectors();
|
||||||
if (connector instanceof NetworkConnector) {
|
for (Connector connector : connectors) {
|
||||||
throw new PortInUseException(
|
try {
|
||||||
((NetworkConnector) connector).getPort());
|
connector.start();
|
||||||
|
}
|
||||||
|
catch (BindException ex) {
|
||||||
|
if (connector instanceof NetworkConnector) {
|
||||||
|
throw new PortInUseException(
|
||||||
|
((NetworkConnector) connector).getPort());
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
}
|
}
|
||||||
throw ex;
|
|
||||||
}
|
}
|
||||||
|
this.started = true;
|
||||||
|
JettyEmbeddedServletContainer.logger
|
||||||
|
.info("Jetty started on port(s) " + getActualPortsDescription());
|
||||||
|
}
|
||||||
|
catch (EmbeddedServletContainerException ex) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new EmbeddedServletContainerException(
|
||||||
|
"Unable to start embedded Jetty servlet container", ex);
|
||||||
}
|
}
|
||||||
JettyEmbeddedServletContainer.logger
|
|
||||||
.info("Jetty started on port(s) " + getActualPortsDescription());
|
|
||||||
}
|
|
||||||
catch (EmbeddedServletContainerException ex) {
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new EmbeddedServletContainerException(
|
|
||||||
"Unable to start embedded Jetty servlet container", ex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +205,10 @@ public class JettyEmbeddedServletContainer implements EmbeddedServletContainer {
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
synchronized (this.monitor) {
|
synchronized (this.monitor) {
|
||||||
|
if (!this.started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.started = false;
|
||||||
try {
|
try {
|
||||||
this.server.stop();
|
this.server.stop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,8 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||||
|
|
||||||
private final boolean autoStart;
|
private final boolean autoStart;
|
||||||
|
|
||||||
|
private volatile boolean started;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link TomcatEmbeddedServletContainer} instance.
|
* Create a new {@link TomcatEmbeddedServletContainer} instance.
|
||||||
* @param tomcat the underlying Tomcat server
|
* @param tomcat the underlying Tomcat server
|
||||||
|
@ -174,28 +176,34 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws EmbeddedServletContainerException {
|
public void start() throws EmbeddedServletContainerException {
|
||||||
try {
|
synchronized (this.monitor) {
|
||||||
addPreviouslyRemovedConnectors();
|
if (this.started) {
|
||||||
Connector connector = this.tomcat.getConnector();
|
return;
|
||||||
if (connector != null && this.autoStart) {
|
}
|
||||||
startConnector(connector);
|
try {
|
||||||
|
addPreviouslyRemovedConnectors();
|
||||||
|
Connector connector = this.tomcat.getConnector();
|
||||||
|
if (connector != null && this.autoStart) {
|
||||||
|
startConnector(connector);
|
||||||
|
}
|
||||||
|
checkThatConnectorsHaveStarted();
|
||||||
|
this.started = true;
|
||||||
|
TomcatEmbeddedServletContainer.logger
|
||||||
|
.info("Tomcat started on port(s): " + getPortsDescription(true));
|
||||||
|
}
|
||||||
|
catch (ConnectorStartFailedException ex) {
|
||||||
|
stopSilently();
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new EmbeddedServletContainerException(
|
||||||
|
"Unable to start embedded Tomcat servlet container", ex);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Context context = findContext();
|
||||||
|
ContextBindings.unbindClassLoader(context, getNamingToken(context),
|
||||||
|
getClass().getClassLoader());
|
||||||
}
|
}
|
||||||
checkThatConnectorsHaveStarted();
|
|
||||||
TomcatEmbeddedServletContainer.logger
|
|
||||||
.info("Tomcat started on port(s): " + getPortsDescription(true));
|
|
||||||
}
|
|
||||||
catch (ConnectorStartFailedException ex) {
|
|
||||||
stopSilently();
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new EmbeddedServletContainerException(
|
|
||||||
"Unable to start embedded Tomcat servlet container", ex);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Context context = findContext();
|
|
||||||
ContextBindings.unbindClassLoader(context, getNamingToken(context),
|
|
||||||
getClass().getClassLoader());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +279,11 @@ public class TomcatEmbeddedServletContainer implements EmbeddedServletContainer
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws EmbeddedServletContainerException {
|
public void stop() throws EmbeddedServletContainerException {
|
||||||
synchronized (this.monitor) {
|
synchronized (this.monitor) {
|
||||||
|
if (!this.started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
this.started = false;
|
||||||
try {
|
try {
|
||||||
stopTomcat();
|
stopTomcat();
|
||||||
this.tomcat.destroy();
|
this.tomcat.destroy();
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
|
||||||
|
|
||||||
private Undertow undertow;
|
private Undertow undertow;
|
||||||
|
|
||||||
private boolean started = false;
|
private volatile boolean started = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link UndertowEmbeddedServletContainer} instance.
|
* Create a new {@link UndertowEmbeddedServletContainer} instance.
|
||||||
|
@ -201,6 +201,9 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
|
||||||
@Override
|
@Override
|
||||||
public void start() throws EmbeddedServletContainerException {
|
public void start() throws EmbeddedServletContainerException {
|
||||||
synchronized (this.monitor) {
|
synchronized (this.monitor) {
|
||||||
|
if (this.started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (!this.autoStart) {
|
if (!this.autoStart) {
|
||||||
return;
|
return;
|
||||||
|
@ -362,16 +365,17 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
|
||||||
@Override
|
@Override
|
||||||
public void stop() throws EmbeddedServletContainerException {
|
public void stop() throws EmbeddedServletContainerException {
|
||||||
synchronized (this.monitor) {
|
synchronized (this.monitor) {
|
||||||
if (this.started) {
|
if (!this.started) {
|
||||||
try {
|
return;
|
||||||
this.started = false;
|
}
|
||||||
this.manager.stop();
|
this.started = false;
|
||||||
this.undertow.stop();
|
try {
|
||||||
}
|
this.manager.stop();
|
||||||
catch (Exception ex) {
|
this.undertow.stop();
|
||||||
throw new EmbeddedServletContainerException("Unable to stop undertow",
|
}
|
||||||
ex);
|
catch (Exception ex) {
|
||||||
}
|
throw new EmbeddedServletContainerException("Unable to stop undertow",
|
||||||
|
ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ import org.mockito.InOrder;
|
||||||
import org.springframework.boot.ApplicationHome;
|
import org.springframework.boot.ApplicationHome;
|
||||||
import org.springframework.boot.ApplicationTemp;
|
import org.springframework.boot.ApplicationTemp;
|
||||||
import org.springframework.boot.context.embedded.Ssl.ClientAuth;
|
import org.springframework.boot.context.embedded.Ssl.ClientAuth;
|
||||||
|
import org.springframework.boot.testutil.InternalOutputCapture;
|
||||||
import org.springframework.boot.web.servlet.ErrorPage;
|
import org.springframework.boot.web.servlet.ErrorPage;
|
||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
import org.springframework.boot.web.servlet.ServletContextInitializer;
|
||||||
|
@ -120,6 +121,9 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public InternalOutputCapture output = new InternalOutputCapture();
|
||||||
|
|
||||||
protected EmbeddedServletContainer container;
|
protected EmbeddedServletContainer container;
|
||||||
|
|
||||||
private final HttpClientContext httpClientContext = HttpClientContext.create();
|
private final HttpClientContext httpClientContext = HttpClientContext.create();
|
||||||
|
@ -153,6 +157,19 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
|
||||||
assertThat(getResponse(getLocalUrl("/hello"))).isEqualTo("Hello World");
|
assertThat(getResponse(getLocalUrl("/hello"))).isEqualTo("Hello World");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startCalledTwice() throws Exception {
|
||||||
|
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
this.container = factory
|
||||||
|
.getEmbeddedServletContainer(exampleServletRegistration());
|
||||||
|
this.container.start();
|
||||||
|
int port = this.container.getPort();
|
||||||
|
this.container.start();
|
||||||
|
assertThat(this.container.getPort()).isEqualTo(port);
|
||||||
|
assertThat(getResponse(getLocalUrl("/hello"))).isEqualTo("Hello World");
|
||||||
|
assertThat(this.output.toString()).containsOnlyOnce("started on port");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyServerWhenPortIsMinusOne() throws Exception {
|
public void emptyServerWhenPortIsMinusOne() throws Exception {
|
||||||
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
AbstractEmbeddedServletContainerFactory factory = getFactory();
|
||||||
|
|
Loading…
Reference in New Issue