Merge branch '1.5.x' into 2.0.x

pull/13564/head
Andy Wilkinson 7 years ago
commit 4fc0a33094

@ -16,6 +16,7 @@
package org.springframework.boot.web.embedded.undertow;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -28,7 +29,6 @@ import io.undertow.Undertow;
import io.undertow.UndertowOptions;
import io.undertow.server.HttpHandler;
import io.undertow.server.handlers.accesslog.AccessLogHandler;
import io.undertow.server.handlers.accesslog.AccessLogReceiver;
import io.undertow.server.handlers.accesslog.DefaultAccessLogReceiver;
import io.undertow.servlet.api.DeploymentInfo;
import org.xnio.OptionMap;
@ -96,9 +96,8 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
public WebServer getWebServer(
org.springframework.http.server.reactive.HttpHandler httpHandler) {
Undertow.Builder builder = createBuilder(getPort());
HttpHandler handler = createUndertowHandler(httpHandler);
builder.setHandler(handler);
return new UndertowWebServer(builder, getPort() >= 0);
Closeable closeable = configureHandler(builder, httpHandler);
return new UndertowWebServer(builder, getPort() >= 0, closeable);
}
private Undertow.Builder createBuilder(int port) {
@ -127,7 +126,7 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
return builder;
}
private HttpHandler createUndertowHandler(
private Closeable configureHandler(Undertow.Builder builder,
org.springframework.http.server.reactive.HttpHandler httpHandler) {
HttpHandler handler = new UndertowHttpHandlerAdapter(httpHandler);
if (this.useForwardHeaders) {
@ -135,25 +134,39 @@ public class UndertowReactiveWebServerFactory extends AbstractReactiveWebServerF
}
handler = UndertowCompressionConfigurer.configureCompression(getCompression(),
handler);
Closeable closeable = null;
if (isAccessLogEnabled()) {
handler = createAccessLogHandler(handler);
closeable = configureAccessLogHandler(builder, handler);
}
return handler;
else {
builder.setHandler(handler);
}
return closeable;
}
private AccessLogHandler createAccessLogHandler(
io.undertow.server.HttpHandler handler) {
private Closeable configureAccessLogHandler(Undertow.Builder builder,
HttpHandler handler) {
try {
createAccessLogDirectoryIfNecessary();
XnioWorker worker = createWorker();
String prefix = (this.accessLogPrefix != null ? this.accessLogPrefix
: "access_log.");
AccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver(
createWorker(), this.accessLogDirectory, prefix, this.accessLogSuffix,
DefaultAccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver(
worker, this.accessLogDirectory, prefix, this.accessLogSuffix,
this.accessLogRotate);
String formatString = (this.accessLogPattern != null ? this.accessLogPattern
String formatString = ((this.accessLogPattern != null) ? this.accessLogPattern
: "common");
return new AccessLogHandler(handler, accessLogReceiver, formatString,
Undertow.class.getClassLoader());
builder.setHandler(new AccessLogHandler(handler, accessLogReceiver,
formatString, Undertow.class.getClassLoader()));
return () -> {
try {
accessLogReceiver.close();
worker.shutdown();
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
};
}
catch (IOException ex) {
throw new IllegalStateException("Failed to create AccessLogHandler", ex);

@ -25,11 +25,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException;
import io.undertow.Undertow;
@ -48,6 +51,7 @@ import io.undertow.server.session.SessionManager;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.DeploymentManager;
import io.undertow.servlet.api.ListenerInfo;
import io.undertow.servlet.api.MimeMapping;
import io.undertow.servlet.api.ServletContainerInitializerInfo;
import io.undertow.servlet.api.ServletStackTraces;
@ -292,27 +296,35 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
}
private void configureAccessLog(DeploymentInfo deploymentInfo) {
deploymentInfo.addInitialHandlerChainWrapper(this::createAccessLogHandler);
}
private AccessLogHandler createAccessLogHandler(HttpHandler handler) {
try {
createAccessLogDirectoryIfNecessary();
XnioWorker worker = createWorker();
String prefix = (this.accessLogPrefix != null ? this.accessLogPrefix
: "access_log.");
AccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver(
createWorker(), this.accessLogDirectory, prefix, this.accessLogSuffix,
DefaultAccessLogReceiver accessLogReceiver = new DefaultAccessLogReceiver(
worker, this.accessLogDirectory, prefix, this.accessLogSuffix,
this.accessLogRotate);
String formatString = (this.accessLogPattern != null ? this.accessLogPattern
: "common");
return new AccessLogHandler(handler, accessLogReceiver, formatString,
Undertow.class.getClassLoader());
EventListener listener = new AccessLogShutdownListener(worker,
accessLogReceiver);
deploymentInfo.addListener(new ListenerInfo(AccessLogShutdownListener.class,
new ImmediateInstanceFactory<EventListener>(listener)));
deploymentInfo.addInitialHandlerChainWrapper(
(handler) -> createAccessLogHandler(handler, accessLogReceiver));
}
catch (IOException ex) {
throw new IllegalStateException("Failed to create AccessLogHandler", ex);
}
}
private AccessLogHandler createAccessLogHandler(HttpHandler handler,
AccessLogReceiver accessLogReceiver) {
createAccessLogDirectoryIfNecessary();
String formatString = ((this.accessLogPattern != null) ? this.accessLogPattern
: "common");
return new AccessLogHandler(handler, accessLogReceiver, formatString,
Undertow.class.getClassLoader());
}
private void createAccessLogDirectoryIfNecessary() {
Assert.state(this.accessLogDirectory != null, "Access log directory is not set");
if (!this.accessLogDirectory.isDirectory() && !this.accessLogDirectory.mkdirs()) {
@ -645,4 +657,33 @@ public class UndertowServletWebServerFactory extends AbstractServletWebServerFac
}
private static class AccessLogShutdownListener implements ServletContextListener {
private final XnioWorker worker;
private final DefaultAccessLogReceiver accessLogReceiver;
AccessLogShutdownListener(XnioWorker worker,
DefaultAccessLogReceiver accessLogReceiver) {
this.worker = worker;
this.accessLogReceiver = accessLogReceiver;
}
@Override
public void contextInitialized(ServletContextEvent sce) {
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
try {
this.accessLogReceiver.close();
this.worker.shutdown();
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
}

@ -16,6 +16,7 @@
package org.springframework.boot.web.embedded.undertow;
import java.io.Closeable;
import java.lang.reflect.Field;
import java.net.BindException;
import java.net.InetSocketAddress;
@ -56,6 +57,8 @@ public class UndertowWebServer implements WebServer {
private final boolean autoStart;
private final Closeable closeable;
private Undertow undertow;
private volatile boolean started = false;
@ -66,8 +69,21 @@ public class UndertowWebServer implements WebServer {
* @param autoStart if the server should be started
*/
public UndertowWebServer(Undertow.Builder builder, boolean autoStart) {
this(builder, autoStart, null);
}
/**
* Create a new {@link UndertowWebServer} instance.
* @param builder the builder
* @param autoStart if the server should be started
* @param closeable called when the server is stopped
* @since 2.0.4
*/
public UndertowWebServer(Undertow.Builder builder, boolean autoStart,
Closeable closeable) {
this.builder = builder;
this.autoStart = autoStart;
this.closeable = closeable;
}
@Override
@ -112,6 +128,7 @@ public class UndertowWebServer implements WebServer {
try {
if (this.undertow != null) {
this.undertow.stop();
this.closeable.close();
}
}
catch (Exception ex) {
@ -214,6 +231,9 @@ public class UndertowWebServer implements WebServer {
this.started = false;
try {
this.undertow.stop();
if (this.closeable != null) {
this.closeable.close();
}
}
catch (Exception ex) {
throw new WebServerException("Unable to stop undertow", ex);

@ -16,15 +16,25 @@
package org.springframework.boot.web.embedded.undertow;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;
import io.undertow.Undertow;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.InOrder;
import reactor.core.publisher.Mono;
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
@ -38,6 +48,9 @@ import static org.mockito.Mockito.mock;
public class UndertowReactiveWebServerFactoryTests
extends AbstractReactiveWebServerFactoryTests {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Override
protected UndertowReactiveWebServerFactory getFactory() {
return new UndertowReactiveWebServerFactory(0);
@ -76,4 +89,44 @@ public class UndertowReactiveWebServerFactoryTests
}
}
@Test
public void accessLogCanBeEnabled()
throws IOException, URISyntaxException, InterruptedException {
testAccessLog(null, null, "access_log.log");
}
@Test
public void accessLogCanBeCustomized()
throws IOException, URISyntaxException, InterruptedException {
testAccessLog("my_access.", "logz", "my_access.logz");
}
private void testAccessLog(String prefix, String suffix, String expectedFile)
throws IOException, URISyntaxException, InterruptedException {
UndertowReactiveWebServerFactory factory = getFactory();
factory.setAccessLogEnabled(true);
factory.setAccessLogPrefix(prefix);
factory.setAccessLogSuffix(suffix);
File accessLogDirectory = this.temporaryFolder.getRoot();
factory.setAccessLogDirectory(accessLogDirectory);
assertThat(accessLogDirectory.listFiles()).isEmpty();
this.webServer = factory.getWebServer(new EchoHandler());
this.webServer.start();
WebClient client = getWebClient().build();
Mono<String> result = client.post().uri("/test").contentType(MediaType.TEXT_PLAIN)
.body(BodyInserters.fromObject("Hello World")).exchange()
.flatMap((response) -> response.bodyToMono(String.class));
assertThat(result.block()).isEqualTo("Hello World");
File accessLog = new File(accessLogDirectory, expectedFile);
awaitFile(accessLog);
assertThat(accessLogDirectory.listFiles()).contains(accessLog);
}
private void awaitFile(File file) throws InterruptedException {
long end = System.currentTimeMillis() + 10000;
while (!file.exists() && System.currentTimeMillis() < end) {
Thread.sleep(100);
}
}
}

Loading…
Cancel
Save