Skip to content

Commit c42571b

Browse files
wilkinsonaphilwebb
andcommitted
Consolidate Undertow WebServers and simplify their constructors
Closes gh-21391 Co-authored-by: Phillip Webb <[email protected]>
1 parent 0d00947 commit c42571b

11 files changed

+823
-821
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.web.embedded.undertow;
18+
19+
import java.io.Closeable;
20+
import java.io.File;
21+
import java.io.IOException;
22+
import java.util.concurrent.TimeUnit;
23+
24+
import io.undertow.Undertow;
25+
import io.undertow.server.HttpHandler;
26+
import io.undertow.server.handlers.accesslog.AccessLogHandler;
27+
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
28+
import org.xnio.OptionMap;
29+
import org.xnio.Options;
30+
import org.xnio.Xnio;
31+
import org.xnio.XnioWorker;
32+
33+
import org.springframework.util.Assert;
34+
35+
/**
36+
* A {@link HttpHandlerFactory} for an {@link AccessLogHandler}.
37+
*
38+
* @author Andy Wilkinson
39+
*/
40+
class AccessLogHttpHandlerFactory implements HttpHandlerFactory {
41+
42+
private final File directory;
43+
44+
private final String pattern;
45+
46+
private final String prefix;
47+
48+
private final String suffix;
49+
50+
private final boolean rotate;
51+
52+
AccessLogHttpHandlerFactory(File directory, String pattern, String prefix, String suffix, boolean rotate) {
53+
this.directory = directory;
54+
this.pattern = pattern;
55+
this.prefix = prefix;
56+
this.suffix = suffix;
57+
this.rotate = rotate;
58+
}
59+
60+
@Override
61+
public HttpHandler getHandler(HttpHandler next) {
62+
try {
63+
createAccessLogDirectoryIfNecessary();
64+
XnioWorker worker = createWorker();
65+
String baseName = (this.prefix != null) ? this.prefix : "access_log.";
66+
String formatString = (this.pattern != null) ? this.pattern : "common";
67+
return new ClosableAccessLogHandler(next, worker,
68+
new DefaultAccessLogReceiver(worker, this.directory, baseName, this.suffix, this.rotate),
69+
formatString);
70+
}
71+
catch (IOException ex) {
72+
throw new IllegalStateException("Failed to create AccessLogHandler", ex);
73+
}
74+
}
75+
76+
private void createAccessLogDirectoryIfNecessary() {
77+
Assert.state(this.directory != null, "Access log directory is not set");
78+
if (!this.directory.isDirectory() && !this.directory.mkdirs()) {
79+
throw new IllegalStateException("Failed to create access log directory '" + this.directory + "'");
80+
}
81+
}
82+
83+
private XnioWorker createWorker() throws IOException {
84+
Xnio xnio = Xnio.getInstance(Undertow.class.getClassLoader());
85+
return xnio.createWorker(OptionMap.builder().set(Options.THREAD_DAEMON, true).getMap());
86+
}
87+
88+
/**
89+
* {@link Closeable} variant of {@link AccessLogHandler}.
90+
*/
91+
private static class ClosableAccessLogHandler extends AccessLogHandler implements Closeable {
92+
93+
private final DefaultAccessLogReceiver accessLogReceiver;
94+
95+
private final XnioWorker worker;
96+
97+
ClosableAccessLogHandler(HttpHandler next, XnioWorker worker, DefaultAccessLogReceiver accessLogReceiver,
98+
String formatString) {
99+
super(next, accessLogReceiver, formatString, Undertow.class.getClassLoader());
100+
this.worker = worker;
101+
this.accessLogReceiver = accessLogReceiver;
102+
}
103+
104+
@Override
105+
public void close() throws IOException {
106+
try {
107+
this.accessLogReceiver.close();
108+
this.worker.shutdown();
109+
this.worker.awaitTermination(30, TimeUnit.SECONDS);
110+
}
111+
catch (IOException ex) {
112+
throw new RuntimeException(ex);
113+
}
114+
catch (InterruptedException ex) {
115+
Thread.currentThread().interrupt();
116+
}
117+
}
118+
119+
}
120+
121+
}
Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,28 @@
3737
import org.springframework.util.MimeTypeUtils;
3838

3939
/**
40-
* Configure the HTTP compression on an Undertow {@link HttpHandler}.
40+
* {@link HttpHandlerFactory} that adds a compression handler.
4141
*
4242
* @author Andy Wilkinson
4343
* @author Phillip Webb
4444
*/
45-
final class UndertowCompressionConfigurer {
45+
class CompressionHttpHandlerFactory implements HttpHandlerFactory {
4646

47-
private UndertowCompressionConfigurer() {
47+
private final Compression compression;
48+
49+
CompressionHttpHandlerFactory(Compression compression) {
50+
this.compression = compression;
4851
}
4952

50-
/**
51-
* Optionally wrap the given {@link HttpHandler} for HTTP compression support.
52-
* @param compression the HTTP compression configuration
53-
* @param httpHandler the HTTP handler to wrap
54-
* @return the wrapped HTTP handler if compression is enabled, or the handler itself
55-
*/
56-
static HttpHandler configureCompression(Compression compression, HttpHandler httpHandler) {
57-
if (compression == null || !compression.getEnabled()) {
58-
return httpHandler;
53+
@Override
54+
public HttpHandler getHandler(HttpHandler next) {
55+
if (!this.compression.getEnabled()) {
56+
return next;
5957
}
6058
ContentEncodingRepository repository = new ContentEncodingRepository();
6159
repository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
62-
Predicates.and(getCompressionPredicates(compression)));
63-
return new EncodingHandler(repository).setNext(httpHandler);
60+
Predicates.and(getCompressionPredicates(this.compression)));
61+
return new EncodingHandler(repository).setNext(next);
6462
}
6563

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

77+
/**
78+
* Predicate used to match specific mime types.
79+
*/
7980
private static class CompressibleMimeTypePredicate implements Predicate {
8081

8182
private final List<MimeType> mimeTypes;

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/ConfigurableUndertowWebServerFactory.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.web.embedded.undertow;
1818

1919
import java.io.File;
20+
import java.util.Collection;
2021

2122
import io.undertow.Undertow.Builder;
2223

@@ -32,6 +33,14 @@
3233
*/
3334
public interface ConfigurableUndertowWebServerFactory extends ConfigurableWebServerFactory {
3435

36+
/**
37+
* Set {@link UndertowBuilderCustomizer}s that should be applied to the Undertow
38+
* {@link Builder}. Calling this method will replace any existing customizers.
39+
* @param customizers the customizers to set
40+
* @since 2.3.0
41+
*/
42+
void setBuilderCustomizers(Collection<? extends UndertowBuilderCustomizer> customizers);
43+
3544
/**
3645
* Add {@link UndertowBuilderCustomizer}s that should be used to customize the
3746
* Undertow {@link Builder}.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.web.embedded.undertow;
18+
19+
import java.io.Closeable;
20+
import java.io.IOException;
21+
22+
import javax.servlet.ServletException;
23+
24+
import io.undertow.server.HttpHandler;
25+
import io.undertow.server.HttpServerExchange;
26+
import io.undertow.servlet.api.DeploymentManager;
27+
28+
import org.springframework.util.Assert;
29+
30+
/**
31+
* {@link HttpHandlerFactory} that for a {@link DeploymentManager}.
32+
*
33+
* @author Andy Wilkinson
34+
* @author Phillip Webb
35+
*/
36+
class DeploymentManagerHttpHandlerFactory implements HttpHandlerFactory {
37+
38+
private final DeploymentManager deploymentManager;
39+
40+
DeploymentManagerHttpHandlerFactory(DeploymentManager deploymentManager) {
41+
this.deploymentManager = deploymentManager;
42+
}
43+
44+
@Override
45+
public HttpHandler getHandler(HttpHandler next) {
46+
Assert.state(next == null, "DeploymentManagerHttpHandlerFactory must be first");
47+
return new DeploymentManagerHandler(this.deploymentManager);
48+
}
49+
50+
DeploymentManager getDeploymentManager() {
51+
return this.deploymentManager;
52+
}
53+
54+
/**
55+
* {@link HttpHandler} that delegates to a {@link DeploymentManager}.
56+
*/
57+
static class DeploymentManagerHandler implements HttpHandler, Closeable {
58+
59+
private final DeploymentManager deploymentManager;
60+
61+
private final HttpHandler handler;
62+
63+
DeploymentManagerHandler(DeploymentManager deploymentManager) {
64+
this.deploymentManager = deploymentManager;
65+
try {
66+
this.handler = deploymentManager.start();
67+
}
68+
catch (ServletException ex) {
69+
throw new RuntimeException(ex);
70+
}
71+
}
72+
73+
@Override
74+
public void handleRequest(HttpServerExchange exchange) throws Exception {
75+
this.handler.handleRequest(exchange);
76+
}
77+
78+
@Override
79+
public void close() throws IOException {
80+
try {
81+
this.deploymentManager.stop();
82+
this.deploymentManager.undeploy();
83+
}
84+
catch (ServletException ex) {
85+
throw new RuntimeException(ex);
86+
}
87+
}
88+
89+
DeploymentManager getDeploymentManager() {
90+
return this.deploymentManager;
91+
}
92+
93+
}
94+
95+
}
Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,41 +18,41 @@
1818

1919
import java.time.Duration;
2020

21+
import io.undertow.server.HttpHandler;
2122
import io.undertow.server.handlers.GracefulShutdownHandler;
2223
import org.apache.commons.logging.Log;
2324
import org.apache.commons.logging.LogFactory;
2425

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

2728
/**
28-
* {@link GracefulShutdown} for Undertow.
29+
* A {@link GracefulShutdownHandler} with support for our own {@link GracefulShutdown}
30+
* interface.
2931
*
3032
* @author Andy Wilkinson
3133
*/
32-
class UndertowGracefulShutdown implements GracefulShutdown {
34+
class GracefulShutdownHttpHandler extends GracefulShutdownHandler implements GracefulShutdown {
3335

34-
private static final Log logger = LogFactory.getLog(UndertowGracefulShutdown.class);
36+
private static final Log logger = LogFactory.getLog(GracefulShutdownHttpHandler.class);
3537

36-
private final GracefulShutdownHandler gracefulShutdownHandler;
37-
38-
private final Duration period;
38+
private final Duration gracePeriod;
3939

4040
private volatile boolean shuttingDown;
4141

42-
UndertowGracefulShutdown(GracefulShutdownHandler gracefulShutdownHandler, Duration period) {
43-
this.gracefulShutdownHandler = gracefulShutdownHandler;
44-
this.period = period;
42+
GracefulShutdownHttpHandler(HttpHandler next, Duration period) {
43+
super(next);
44+
this.gracePeriod = period;
4545
}
4646

4747
@Override
4848
public boolean shutDownGracefully() {
49-
logger.info("Commencing graceful shutdown, allowing up to " + this.period.getSeconds()
49+
logger.info("Commencing graceful shutdown, allowing up to " + this.gracePeriod.getSeconds()
5050
+ "s for active requests to complete");
51-
this.gracefulShutdownHandler.shutdown();
51+
shutdown();
5252
this.shuttingDown = true;
5353
boolean graceful = false;
5454
try {
55-
graceful = this.gracefulShutdownHandler.awaitShutdown(this.period.toMillis());
55+
graceful = awaitShutdown(this.gracePeriod.toMillis());
5656
}
5757
catch (InterruptedException ex) {
5858
Thread.currentThread().interrupt();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.web.embedded.undertow;
18+
19+
import java.io.Closeable;
20+
21+
import io.undertow.server.HttpHandler;
22+
23+
import org.springframework.boot.web.server.GracefulShutdown;
24+
25+
/**
26+
* Factory used by {@link UndertowServletWebServer} to add {@link HttpHandler
27+
* HttpHandlers}. Instances returned from this factory may optionally implement the
28+
* following interfaces:
29+
* <ul>
30+
* <li>{@link Closeable} - if they wish to be closed just before server stops.</li>
31+
* <li>{@link GracefulShutdown} - if they wish to manage graceful shutdown.</li>
32+
* </ul>
33+
*
34+
* @author Phillip Webb
35+
* @since 2.3.0
36+
*/
37+
@FunctionalInterface
38+
public interface HttpHandlerFactory {
39+
40+
/**
41+
* Create the {@link HttpHandler} instance that should be added.
42+
* @param next the next handler in the chain
43+
* @return the new HTTP handler instance
44+
*/
45+
HttpHandler getHandler(HttpHandler next);
46+
47+
}

0 commit comments

Comments
 (0)