Commit df5f5912 authored by Andy Wilkinson's avatar Andy Wilkinson

Support Jetty 10

Closes gh-24886
parent a95e93a8
...@@ -59,6 +59,9 @@ Spring Boot supports the following embedded servlet containers: ...@@ -59,6 +59,9 @@ Spring Boot supports the following embedded servlet containers:
| Jetty 9.4 | Jetty 9.4
| 3.1 | 3.1
| Jetty 10.0
| 4.0
| Undertow 2.0 | Undertow 2.0
| 4.0 | 4.0
|=== |===
......
...@@ -451,7 +451,8 @@ The following Maven example shows how to exclude Tomcat and include Jetty for Sp ...@@ -451,7 +451,8 @@ The following Maven example shows how to exclude Tomcat and include Jetty for Sp
</dependency> </dependency>
---- ----
NOTE: The version of the Servlet API has been overridden as, unlike Tomcat 9 and Undertow 2.0, Jetty 9.4 does not support Servlet 4.0. NOTE: The version of the Servlet API has been overridden as, unlike Tomcat 9 and Undertow 2, Jetty 9.4 does not support Servlet 4.0.
If you wish to use Jetty 10, which does support Servlet 4.0, override the `jetty.version` property rather than the `servlet-api.version` property.
The following Gradle example shows how to use Undertow in place of Reactor Netty for Spring WebFlux: The following Gradle example shows how to use Undertow in place of Reactor Netty for Spring WebFlux:
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
package org.springframework.boot.web.embedded.jetty; package org.springframework.boot.web.embedded.jetty;
import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
...@@ -27,6 +30,7 @@ import org.eclipse.jetty.server.Server; ...@@ -27,6 +30,7 @@ import org.eclipse.jetty.server.Server;
import org.springframework.boot.web.server.GracefulShutdownCallback; import org.springframework.boot.web.server.GracefulShutdownCallback;
import org.springframework.boot.web.server.GracefulShutdownResult; import org.springframework.boot.web.server.GracefulShutdownResult;
import org.springframework.core.log.LogMessage; import org.springframework.core.log.LogMessage;
import org.springframework.util.ReflectionUtils;
/** /**
* Handles Jetty graceful shutdown. * Handles Jetty graceful shutdown.
...@@ -50,17 +54,28 @@ final class GracefulShutdown { ...@@ -50,17 +54,28 @@ final class GracefulShutdown {
void shutDownGracefully(GracefulShutdownCallback callback) { void shutDownGracefully(GracefulShutdownCallback callback) {
logger.info("Commencing graceful shutdown. Waiting for active requests to complete"); logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
boolean jetty10 = isJetty10();
for (Connector connector : this.server.getConnectors()) { for (Connector connector : this.server.getConnectors()) {
shutdown(connector); shutdown(connector, !jetty10);
} }
this.shuttingDown = true; this.shuttingDown = true;
new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start(); new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start();
} }
private void shutdown(Connector connector) { @SuppressWarnings("unchecked")
private void shutdown(Connector connector, boolean getResult) {
Future<Void> result;
try { try {
connector.shutdown().get(); result = connector.shutdown();
}
catch (NoSuchMethodError ex) {
Method shutdown = ReflectionUtils.findMethod(connector.getClass(), "shutdown");
result = (Future<Void>) ReflectionUtils.invokeMethod(shutdown, connector);
}
if (getResult) {
try {
result.get();
} }
catch (InterruptedException ex) { catch (InterruptedException ex) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
...@@ -69,6 +84,16 @@ final class GracefulShutdown { ...@@ -69,6 +84,16 @@ final class GracefulShutdown {
// Continue // Continue
} }
} }
}
private boolean isJetty10() {
try {
return CompletableFuture.class.equals(Connector.class.getMethod("shutdown").getReturnType());
}
catch (Exception ex) {
return false;
}
}
private void awaitShutdown(GracefulShutdownCallback callback) { private void awaitShutdown(GracefulShutdownCallback callback) {
while (this.shuttingDown && this.activeRequests.get() > 0) { while (this.shuttingDown && this.activeRequests.get() > 0) {
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -21,6 +21,7 @@ import java.util.Arrays; ...@@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
...@@ -49,11 +50,11 @@ class JettyEmbeddedErrorHandler extends ErrorPageErrorHandler { ...@@ -49,11 +50,11 @@ class JettyEmbeddedErrorHandler extends ErrorPageErrorHandler {
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException { throws IOException, ServletException {
if (!HANDLED_HTTP_METHODS.contains(baseRequest.getMethod())) { if (!HANDLED_HTTP_METHODS.contains(baseRequest.getMethod())) {
baseRequest.setMethod("GET"); baseRequest.setMethod("GET");
} }
super.doError(target, baseRequest, request, response); super.handle(target, baseRequest, request, response);
} }
} }
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -47,8 +47,13 @@ final class JettyHandlerWrappers { ...@@ -47,8 +47,13 @@ final class JettyHandlerWrappers {
handler.addIncludedMethods(httpMethod.name()); handler.addIncludedMethods(httpMethod.name());
} }
if (compression.getExcludedUserAgents() != null) { if (compression.getExcludedUserAgents() != null) {
try {
handler.setExcludedAgentPatterns(compression.getExcludedUserAgents()); handler.setExcludedAgentPatterns(compression.getExcludedUserAgents());
} }
catch (NoSuchMethodError ex) {
// Jetty 10 does not support User-Agent-based exclusions
}
}
return handler; return handler;
} }
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -19,6 +19,7 @@ package org.springframework.boot.web.embedded.jetty; ...@@ -19,6 +19,7 @@ package org.springframework.boot.web.embedded.jetty;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
...@@ -70,6 +71,7 @@ import org.springframework.boot.web.servlet.server.ServletWebServerFactory; ...@@ -70,6 +71,7 @@ import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
...@@ -306,7 +308,16 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor ...@@ -306,7 +308,16 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
holder.setInitParameter("dirAllowed", "false"); holder.setInitParameter("dirAllowed", "false");
holder.setInitOrder(1); holder.setInitOrder(1);
context.getServletHandler().addServletWithMapping(holder, "/"); context.getServletHandler().addServletWithMapping(holder, "/");
context.getServletHandler().getServletMapping("/").setDefault(true); ServletMapping servletMapping = context.getServletHandler().getServletMapping("/");
try {
servletMapping.setDefault(true);
}
catch (NoSuchMethodError ex) {
// Jetty 10
Method setFromDefaultDescriptor = ReflectionUtils.findMethod(servletMapping.getClass(),
"setFromDefaultDescriptor", boolean.class);
ReflectionUtils.invokeMethod(setFromDefaultDescriptor, servletMapping, true);
}
} }
/** /**
......
...@@ -32,7 +32,6 @@ import org.eclipse.jetty.server.handler.ContextHandler; ...@@ -32,7 +32,6 @@ import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.springframework.boot.web.server.GracefulShutdownCallback; import org.springframework.boot.web.server.GracefulShutdownCallback;
import org.springframework.boot.web.server.GracefulShutdownResult; import org.springframework.boot.web.server.GracefulShutdownResult;
...@@ -120,18 +119,7 @@ public class JettyWebServer implements WebServer { ...@@ -120,18 +119,7 @@ public class JettyWebServer implements WebServer {
// Cache the connectors and then remove them to prevent requests being // Cache the connectors and then remove them to prevent requests being
// handled before the application context is ready. // handled before the application context is ready.
this.connectors = this.server.getConnectors(); this.connectors = this.server.getConnectors();
this.server.addBean(new AbstractLifeCycle() {
@Override
protected void doStart() throws Exception {
for (Connector connector : JettyWebServer.this.connectors) {
Assert.state(connector.isStopped(),
() -> "Connector " + connector + " has been started prematurely");
}
JettyWebServer.this.server.setConnectors(null); JettyWebServer.this.server.setConnectors(null);
}
});
// Start the server so that the ServletContext is available // Start the server so that the ServletContext is available
this.server.start(); this.server.start();
this.server.setStopAtShutdown(false); this.server.setStopAtShutdown(false);
......
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -106,8 +106,22 @@ class SslServerCustomizer implements JettyServerCustomizer { ...@@ -106,8 +106,22 @@ class SslServerCustomizer implements JettyServerCustomizer {
private ServerConnector createHttp11ServerConnector(Server server, HttpConfiguration config, private ServerConnector createHttp11ServerConnector(Server server, HttpConfiguration config,
SslContextFactory.Server sslContextFactory) { SslContextFactory.Server sslContextFactory) {
HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config); HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config);
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, SslConnectionFactory sslConnectionFactory;
HttpVersion.HTTP_1_1.asString()); try {
sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString());
}
catch (NoSuchMethodError ex) {
// Jetty 10
try {
sslConnectionFactory = SslConnectionFactory.class
.getConstructor(SslContextFactory.Server.class, String.class)
.newInstance(sslContextFactory, HttpVersion.HTTP_1_1.asString());
}
catch (Exception ex2) {
throw new RuntimeException(ex2);
}
}
return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), sslConnectionFactory, return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), sslConnectionFactory,
connectionFactory); connectionFactory);
} }
......
/*
* Copyright 2012-2021 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.jetty;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import org.springframework.boot.testsupport.classpath.ClassPathOverrides;
import static org.assertj.core.api.Assertions.assertThat;
@EnabledForJreRange(min = JRE.JAVA_11)
@ClassPathExclusions({ "jetty-*.jar", "tomcat-embed*.jar" })
@ClassPathOverrides({ "org.slf4j:slf4j-api:1.7.25", "org.eclipse.jetty:jetty-io:10.0.0",
"org.eclipse.jetty:jetty-server:10.0.0", "org.eclipse.jetty:jetty-servlet:10.0.0",
"org.eclipse.jetty:jetty-util:10.0.0", "org.eclipse.jetty:jetty-webapp:10.0.0",
"org.eclipse.jetty.http2:http2-common:10.0.0", "org.eclipse.jetty.http2:http2-hpack:10.0.0",
"org.eclipse.jetty.http2:http2-server:10.0.0", "org.mortbay.jasper:apache-jsp:8.5.40" })
public class Jetty10ServletWebServerFactoryTests extends JettyServletWebServerFactoryTests {
@Override
@Test
protected void correctVersionOfJettyUsed() {
String jettyVersion = ErrorHandler.class.getPackage().getImplementationVersion();
assertThat(jettyVersion.startsWith("10.0"));
}
@Override
@Disabled("Jetty 10 does not support User-Agent-based compression")
protected void noCompressionForUserAgent() {
}
@Override
@Disabled("Jetty 10 adds methods to Configuration that we can't mock while compiling against 9")
protected void jettyConfigurations() throws Exception {
}
}
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
package org.springframework.boot.web.embedded.jetty; package org.springframework.boot.web.embedded.jetty;
import java.lang.reflect.Method;
import java.net.InetAddress; import java.net.InetAddress;
import java.time.Duration; import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.EventListener;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
...@@ -46,6 +48,7 @@ import org.eclipse.jetty.server.handler.ErrorHandler; ...@@ -46,6 +48,7 @@ import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jetty.webapp.AbstractConfiguration; import org.eclipse.jetty.webapp.AbstractConfiguration;
...@@ -59,6 +62,7 @@ import org.springframework.boot.web.server.Shutdown; ...@@ -59,6 +62,7 @@ import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerException; import org.springframework.boot.web.server.WebServerException;
import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
...@@ -77,7 +81,7 @@ import static org.mockito.Mockito.mock; ...@@ -77,7 +81,7 @@ import static org.mockito.Mockito.mock;
class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFactoryTests { class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFactoryTests {
@Test @Test
void correctVersionOfJettyUsed() { protected void correctVersionOfJettyUsed() {
String jettyVersion = ErrorHandler.class.getPackage().getImplementationVersion(); String jettyVersion = ErrorHandler.class.getPackage().getImplementationVersion();
Matcher matcher = Pattern.compile("[0-9]+.[0-9]+.([0-9]+)[\\.-].*").matcher(jettyVersion); Matcher matcher = Pattern.compile("[0-9]+.[0-9]+.([0-9]+)[\\.-].*").matcher(jettyVersion);
assertThat(matcher.find()).isTrue(); assertThat(matcher.find()).isTrue();
...@@ -85,7 +89,7 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac ...@@ -85,7 +89,7 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac
} }
@Test @Test
void jettyConfigurations() throws Exception { protected void jettyConfigurations() throws Exception {
JettyServletWebServerFactory factory = getFactory(); JettyServletWebServerFactory factory = getFactory();
Configuration[] configurations = new Configuration[4]; Configuration[] configurations = new Configuration[4];
Arrays.setAll(configurations, (i) -> mock(Configuration.class)); Arrays.setAll(configurations, (i) -> mock(Configuration.class));
...@@ -143,9 +147,9 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac ...@@ -143,9 +147,9 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac
JettyWebServer jettyWebServer = (JettyWebServer) this.webServer; JettyWebServer jettyWebServer = (JettyWebServer) this.webServer;
ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0]; ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0];
SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class); SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class);
assertThat(connectionFactory.getSslContextFactory().getIncludeCipherSuites()).containsExactly("ALPHA", "BRAVO", SslContextFactory sslContextFactory = extractSslContextFactory(connectionFactory);
"CHARLIE"); assertThat(sslContextFactory.getIncludeCipherSuites()).containsExactly("ALPHA", "BRAVO", "CHARLIE");
assertThat(connectionFactory.getSslContextFactory().getExcludeCipherSuites()).isEmpty(); assertThat(sslContextFactory.getExcludeCipherSuites()).isEmpty();
} }
@Test @Test
...@@ -166,8 +170,8 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac ...@@ -166,8 +170,8 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac
JettyWebServer jettyWebServer = (JettyWebServer) this.webServer; JettyWebServer jettyWebServer = (JettyWebServer) this.webServer;
ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0]; ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0];
SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class); SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class);
assertThat(connectionFactory.getSslContextFactory().getIncludeProtocols()).containsExactly("TLSv1.1", SslContextFactory sslContextFactory = extractSslContextFactory(connectionFactory);
"TLSv1.2"); assertThat(sslContextFactory.getIncludeProtocols()).containsExactly("TLSv1.1", "TLSv1.2");
} }
@Test @Test
...@@ -179,7 +183,19 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac ...@@ -179,7 +183,19 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac
JettyWebServer jettyWebServer = (JettyWebServer) this.webServer; JettyWebServer jettyWebServer = (JettyWebServer) this.webServer;
ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0]; ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0];
SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class); SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class);
assertThat(connectionFactory.getSslContextFactory().getIncludeProtocols()).containsExactly("TLSv1.1"); SslContextFactory sslContextFactory = extractSslContextFactory(connectionFactory);
assertThat(sslContextFactory.getIncludeProtocols()).containsExactly("TLSv1.1");
}
private SslContextFactory extractSslContextFactory(SslConnectionFactory connectionFactory) {
try {
return connectionFactory.getSslContextFactory();
}
catch (NoSuchMethodError ex) {
Method getSslContextFactory = ReflectionUtils.findMethod(connectionFactory.getClass(),
"getSslContextFactory");
return (SslContextFactory) ReflectionUtils.invokeMethod(getSslContextFactory, connectionFactory);
}
} }
@Test @Test
...@@ -372,7 +388,7 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac ...@@ -372,7 +388,7 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac
JettyServletWebServerFactory factory = getFactory(); JettyServletWebServerFactory factory = getFactory();
factory.addServerCustomizers((JettyServerCustomizer) (server) -> { factory.addServerCustomizers((JettyServerCustomizer) (server) -> {
Collection<WebAppContext> contexts = server.getBeans(WebAppContext.class); Collection<WebAppContext> contexts = server.getBeans(WebAppContext.class);
contexts.iterator().next().addEventListener(new ServletContextListener() { EventListener eventListener = new ServletContextListener() {
@Override @Override
public void contextInitialized(ServletContextEvent event) { public void contextInitialized(ServletContextEvent event) {
...@@ -382,8 +398,17 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac ...@@ -382,8 +398,17 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac
@Override @Override
public void contextDestroyed(ServletContextEvent event) { public void contextDestroyed(ServletContextEvent event) {
} }
};
}); WebAppContext context = contexts.iterator().next();
try {
context.addEventListener(eventListener);
}
catch (NoSuchMethodError ex) {
// Jetty 10
Method addEventListener = ReflectionUtils.findMethod(context.getClass(), "addEventListener",
EventListener.class);
ReflectionUtils.invokeMethod(addEventListener, context, eventListener);
}
}); });
assertThatExceptionOfType(WebServerException.class).isThrownBy(() -> { assertThatExceptionOfType(WebServerException.class).isThrownBy(() -> {
JettyWebServer jettyWebServer = (JettyWebServer) factory.getWebServer(); JettyWebServer jettyWebServer = (JettyWebServer) factory.getWebServer();
......
...@@ -842,7 +842,7 @@ public abstract class AbstractServletWebServerFactoryTests { ...@@ -842,7 +842,7 @@ public abstract class AbstractServletWebServerFactoryTests {
} }
@Test @Test
void noCompressionForUserAgent() throws Exception { protected void noCompressionForUserAgent() throws Exception {
assertThat(doTestCompression(10000, null, new String[] { "testUserAgent" })).isFalse(); assertThat(doTestCompression(10000, null, new String[] { "testUserAgent" })).isFalse();
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment