Skip to content

Commit

Permalink
Consolidate Undertow WebServers and simplify their constructors
Browse files Browse the repository at this point in the history
Closes spring-projectsgh-21391

Co-authored-by: Phillip Webb <[email protected]>
  • Loading branch information
wilkinsona and philwebb committed May 12, 2020
1 parent 07958ac commit eef3d95
Show file tree
Hide file tree
Showing 11 changed files with 823 additions and 821 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.web.embedded.undertow;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

import io.undertow.Undertow;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.accesslog.AccessLogHandler;
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
import org.xnio.OptionMap;
import org.xnio.Options;
import org.xnio.Xnio;
import org.xnio.XnioWorker;

import org.springframework.util.Assert;

/**
* A {@link HttpHandlerFactory} for an {@link AccessLogHandler}.
*
* @author Andy Wilkinson
*/
class AccessLogHttpHandlerFactory implements HttpHandlerFactory {

private final File directory;

private final String pattern;

private final String prefix;

private final String suffix;

private final boolean rotate;

AccessLogHttpHandlerFactory(File directory, String pattern, String prefix, String suffix, boolean rotate) {
this.directory = directory;
this.pattern = pattern;
this.prefix = prefix;
this.suffix = suffix;
this.rotate = rotate;
}

@Override
public HttpHandler getHandler(HttpHandler next) {
try {
createAccessLogDirectoryIfNecessary();
XnioWorker worker = createWorker();
String baseName = (this.prefix != null) ? this.prefix : "access_log.";
String formatString = (this.pattern != null) ? this.pattern : "common";
return new ClosableAccessLogHandler(next, worker,
new DefaultAccessLogReceiver(worker, this.directory, baseName, this.suffix, this.rotate),
formatString);
}
catch (IOException ex) {
throw new IllegalStateException("Failed to create AccessLogHandler", ex);
}
}

private void createAccessLogDirectoryIfNecessary() {
Assert.state(this.directory != null, "Access log directory is not set");
if (!this.directory.isDirectory() && !this.directory.mkdirs()) {
throw new IllegalStateException("Failed to create access log directory '" + this.directory + "'");
}
}

private XnioWorker createWorker() throws IOException {
Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader());
return xnio.createWorker(OptionMap.builder().set(Options.THREAD_DAEMON, true).getMap());
}

/**
* {@link Closeable} variant of {@link AccessLogHandler}.
*/
private static class ClosableAccessLogHandler extends AccessLogHandler implements Closeable {

private final DefaultAccessLogReceiver accessLogReceiver;

private final XnioWorker worker;

ClosableAccessLogHandler(HttpHandler next, XnioWorker worker, DefaultAccessLogReceiver accessLogReceiver,
String formatString) {
super(next, accessLogReceiver, formatString, Undertow.class.getClassLoader());
this.worker = worker;
this.accessLogReceiver = accessLogReceiver;
}

@Override
public void close() throws IOException {
try {
this.accessLogReceiver.close();
this.worker.shutdown();
this.worker.awaitTermination(30, TimeUnit.SECONDS);
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,28 @@
import org.springframework.util.MimeTypeUtils;

/**
* Configure the HTTP compression on an Undertow {@link HttpHandler}.
* {@link HttpHandlerFactory} that adds a compression handler.
*
* @author Andy Wilkinson
* @author Phillip Webb
*/
final class UndertowCompressionConfigurer {
class CompressionHttpHandlerFactory implements HttpHandlerFactory {

private UndertowCompressionConfigurer() {
private final Compression compression;

CompressionHttpHandlerFactory(Compression compression) {
this.compression = compression;
}

/**
* Optionally wrap the given {@link HttpHandler} for HTTP compression support.
* @param compression the HTTP compression configuration
* @param httpHandler the HTTP handler to wrap
* @return the wrapped HTTP handler if compression is enabled, or the handler itself
*/
static HttpHandler configureCompression(Compression compression, HttpHandler httpHandler) {
if (compression == null || !compression.getEnabled()) {
return httpHandler;
@Override
public HttpHandler getHandler(HttpHandler next) {
if (!this.compression.getEnabled()) {
return next;
}
ContentEncodingRepository repository = new ContentEncodingRepository();
repository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
Predicates.and(getCompressionPredicates(compression)));
return new EncodingHandler(repository).setNext(httpHandler);
Predicates.and(getCompressionPredicates(this.compression)));
return new EncodingHandler(repository).setNext(next);
}

private static Predicate[] getCompressionPredicates(Compression compression) {
Expand All @@ -76,6 +74,9 @@ private static Predicate[] getCompressionPredicates(Compression compression) {
return predicates.toArray(new Predicate[0]);
}

/**
* Predicate used to match specific mime types.
*/
private static class CompressibleMimeTypePredicate implements Predicate {

private final List<MimeType> mimeTypes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.springframework.boot.web.embedded.undertow;

import java.io.File;
import java.util.Collection;

import io.undertow.Undertow.Builder;

Expand All @@ -32,6 +33,14 @@
*/
public interface ConfigurableUndertowWebServerFactory extends ConfigurableWebServerFactory {

/**
* Set {@link UndertowBuilderCustomizer}s that should be applied to the Undertow
* {@link Builder}. Calling this method will replace any existing customizers.
* @param customizers the customizers to set
* @since 2.3.0
*/
void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers);

/**
* Add {@link UndertowBuilderCustomizer}s that should be used to customize the
* Undertow {@link Builder}.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.web.embedded.undertow;

import java.io.Closeable;
import java.io.IOException;

import javax.servlet.ServletException;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.api.DeploymentManager;

import org.springframework.util.Assert;

/**
* {@link HttpHandlerFactory} that for a {@link DeploymentManager}.
*
* @author Andy Wilkinson
* @author Phillip Webb
*/
class DeploymentManagerHttpHandlerFactory implements HttpHandlerFactory {

private final DeploymentManager deploymentManager;

DeploymentManagerHttpHandlerFactory(DeploymentManager deploymentManager) {
this.deploymentManager = deploymentManager;
}

@Override
public HttpHandler getHandler(HttpHandler next) {
Assert.state(next == null, "DeploymentManagerHttpHandlerFactory must be first");
return new DeploymentManagerHandler(this.deploymentManager);
}

DeploymentManager getDeploymentManager() {
return this.deploymentManager;
}

/**
* {@link HttpHandler} that delegates to a {@link DeploymentManager}.
*/
static class DeploymentManagerHandler implements HttpHandler, Closeable {

private final DeploymentManager deploymentManager;

private final HttpHandler handler;

DeploymentManagerHandler(DeploymentManager deploymentManager) {
this.deploymentManager = deploymentManager;
try {
this.handler = deploymentManager.start();
}
catch (ServletException ex) {
throw new RuntimeException(ex);
}
}

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
this.handler.handleRequest(exchange);
}

@Override
public void close() throws IOException {
try {
this.deploymentManager.stop();
this.deploymentManager.undeploy();
}
catch (ServletException ex) {
throw new RuntimeException(ex);
}
}

DeploymentManager getDeploymentManager() {
return this.deploymentManager;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,41 @@

import java.time.Duration;

import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.GracefulShutdownHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.boot.web.server.GracefulShutdown;

/**
* {@link GracefulShutdown} for Undertow.
* A {@link GracefulShutdownHandler} with support for our own {@link GracefulShutdown}
* interface.
*
* @author Andy Wilkinson
*/
class UndertowGracefulShutdown implements GracefulShutdown {
class GracefulShutdownHttpHandler extends GracefulShutdownHandler implements GracefulShutdown {

private static final Log logger = LogFactory.getLog(UndertowGracefulShutdown.class);
private static final Log logger = LogFactory.getLog(GracefulShutdownHttpHandler.class);

private final GracefulShutdownHandler gracefulShutdownHandler;

private final Duration period;
private final Duration gracePeriod;

private volatile boolean shuttingDown;

UndertowGracefulShutdown(GracefulShutdownHandler gracefulShutdownHandler, Duration period) {
this.gracefulShutdownHandler = gracefulShutdownHandler;
this.period = period;
GracefulShutdownHttpHandler(HttpHandler next, Duration period) {
super(next);
this.gracePeriod = period;
}

@Override
public boolean shutDownGracefully() {
logger.info("Commencing graceful shutdown, allowing up to " + this.period.getSeconds()
logger.info("Commencing graceful shutdown, allowing up to " + this.gracePeriod.getSeconds()
+ "s for active requests to complete");
this.gracefulShutdownHandler.shutdown();
shutdown();
this.shuttingDown = true;
boolean graceful = false;
try {
graceful = this.gracefulShutdownHandler.awaitShutdown(this.period.toMillis());
graceful = awaitShutdown(this.gracePeriod.toMillis());
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.web.embedded.undertow;

import java.io.Closeable;

import io.undertow.server.HttpHandler;

import org.springframework.boot.web.server.GracefulShutdown;

/**
* Factory used by {@link UndertowServletWebServer} to add {@link HttpHandler
* HttpHandlers}. Instances returned from this factory may optionally implement the
* following interfaces:
* <ul>
* <li>{@link Closeable} - if they wish to be closed just before server stops.</li>
* <li>{@link GracefulShutdown} - if they wish to manage graceful shutdown.</li>
* </ul>
*
* @author Phillip Webb
* @since 2.3.0
*/
@FunctionalInterface
public interface HttpHandlerFactory {

/**
* Create the {@link HttpHandler} instance that should be added.
* @param next the next handler in the chain
* @return the new HTTP handler instance
*/
HttpHandler getHandler(HttpHandler next);

}
Loading

0 comments on commit eef3d95

Please sign in to comment.