Skip to content

Make stop behavior across various Web Server implementations consistent #44144

Open
@nosan

Description

@nosan

Javadoc states:

Stops the web server. Calling this method on an already stopped server has no effect.

I reviewed all WebServer implementations and identified some inconsistencies among them.

In the case of TomcatWebServer, it consistently tries to removeServiceConnectors, and
stop method can be invoked multiple times.

public void stop() throws WebServerException {
	synchronized (this.monitor) {
		boolean wasStarted = this.started;
		try {
			this.started = false;
			if (this.gracefulShutdown != null) {
				this.gracefulShutdown.abort();
			}
			removeServiceConnectors();
		}
		catch (Exception ex) {
			throw new WebServerException("Unable to stop embedded Tomcat", ex);
		}
		finally {
			if (wasStarted) {
				containerCounter.decrementAndGet();
			}
		}
	}
}

For NettyWebServer, if an error occurs, it silently catches the exception without
throwing a WebServerException. Additionally, subsequent calls to stop() will have no
effect.

    public void stop() throws WebServerException {
	if (this.disposableServer != null) {
		if (this.gracefulShutdown != null) {
			this.gracefulShutdown.abort();
		}
		try {
			if (this.lifecycleTimeout != null) {
				this.disposableServer.disposeNow(this.lifecycleTimeout);
			}
			else {
				this.disposableServer.disposeNow();
			}
		}
		catch (IllegalStateException ex) {
			// Continue
		}
		this.disposableServer = null;
	}
}

Similarly to Tomcat, JettyWebServer attempts to stop its connectors and allows the stop
method to be invoked multiple times.

public void stop() {
	synchronized (this.monitor) {
		this.started = false;
		if (this.gracefulShutdown != null) {
			this.gracefulShutdown.abort();
		}
		try {
			for (Connector connector : this.server.getConnectors()) {
				connector.stop();
			}
		}
		catch (InterruptedException ex) {
			Thread.currentThread().interrupt();
		}
		catch (Exception ex) {
			throw new WebServerException("Unable to stop embedded Jetty server", ex);
		}
	}
}

In contrast, UndertowWebServer makes the second call to stop() ineffective, preventing any retries to stop the
WebServer in case of failure.

@Override
public void stop() throws WebServerException {
	synchronized (this.monitor) {
		if (!this.started) {
			return;
		}
		this.started = false;
		if (this.gracefulShutdown != null) {
			notifyGracefulCallback(false);
		}
		try {
			this.undertow.stop();
			for (Closeable closeable : this.closeables) {
				closeable.close();
			}
		}
		catch (Exception ex) {
			throw new WebServerException("Unable to stop Undertow", ex);
		}
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions