From df5f59127ad7253706003fea461b23f27db5b240 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 4 Feb 2021 14:54:34 +0000 Subject: [PATCH] Support Jetty 10 Closes gh-24886 --- .../src/docs/asciidoc/getting-started.adoc | 3 + .../src/docs/asciidoc/howto.adoc | 3 +- .../web/embedded/jetty/GracefulShutdown.java | 41 ++++++++++--- .../jetty/JettyEmbeddedErrorHandler.java | 7 ++- .../embedded/jetty/JettyHandlerWrappers.java | 9 ++- .../jetty/JettyServletWebServerFactory.java | 15 ++++- .../web/embedded/jetty/JettyWebServer.java | 14 +---- .../embedded/jetty/SslServerCustomizer.java | 20 ++++++- .../Jetty10ServletWebServerFactoryTests.java | 57 +++++++++++++++++++ .../JettyServletWebServerFactoryTests.java | 49 ++++++++++++---- .../AbstractServletWebServerFactoryTests.java | 2 +- 11 files changed, 175 insertions(+), 45 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/Jetty10ServletWebServerFactoryTests.java diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started.adoc index b030d52e22..5ffc5c3759 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started.adoc @@ -59,6 +59,9 @@ Spring Boot supports the following embedded servlet containers: | Jetty 9.4 | 3.1 +| Jetty 10.0 +| 4.0 + | Undertow 2.0 | 4.0 |=== diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc index 78eb8cb4da..8ae3a15535 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc @@ -451,7 +451,8 @@ The following Maven example shows how to exclude Tomcat and include Jetty for Sp ---- -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: diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/GracefulShutdown.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/GracefulShutdown.java index 693f663ade..f7dec21db5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/GracefulShutdown.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/GracefulShutdown.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -16,7 +16,10 @@ 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.Future; import java.util.function.Supplier; import org.apache.commons.logging.Log; @@ -27,6 +30,7 @@ import org.eclipse.jetty.server.Server; import org.springframework.boot.web.server.GracefulShutdownCallback; import org.springframework.boot.web.server.GracefulShutdownResult; import org.springframework.core.log.LogMessage; +import org.springframework.util.ReflectionUtils; /** * Handles Jetty graceful shutdown. @@ -50,23 +54,44 @@ final class GracefulShutdown { void shutDownGracefully(GracefulShutdownCallback callback) { logger.info("Commencing graceful shutdown. Waiting for active requests to complete"); + boolean jetty10 = isJetty10(); for (Connector connector : this.server.getConnectors()) { - shutdown(connector); + shutdown(connector, !jetty10); } this.shuttingDown = true; new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start(); } - private void shutdown(Connector connector) { + @SuppressWarnings("unchecked") + private void shutdown(Connector connector, boolean getResult) { + Future result; try { - connector.shutdown().get(); + result = connector.shutdown(); } - catch (InterruptedException ex) { - Thread.currentThread().interrupt(); + catch (NoSuchMethodError ex) { + Method shutdown = ReflectionUtils.findMethod(connector.getClass(), "shutdown"); + result = (Future) ReflectionUtils.invokeMethod(shutdown, connector); } - catch (ExecutionException ex) { - // Continue + if (getResult) { + try { + result.get(); + } + catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + catch (ExecutionException ex) { + // Continue + } + } + } + + private boolean isJetty10() { + try { + return CompletableFuture.class.equals(Connector.class.getMethod("shutdown").getReturnType()); + } + catch (Exception ex) { + return false; } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java index 8804193a07..f07f95d926 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -49,11 +50,11 @@ class JettyEmbeddedErrorHandler extends ErrorPageErrorHandler { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) - throws IOException { + throws IOException, ServletException { if (!HANDLED_HTTP_METHODS.contains(baseRequest.getMethod())) { baseRequest.setMethod("GET"); } - super.doError(target, baseRequest, request, response); + super.handle(target, baseRequest, request, response); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyHandlerWrappers.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyHandlerWrappers.java index 9f705b535f..a928ef1114 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyHandlerWrappers.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyHandlerWrappers.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -47,7 +47,12 @@ final class JettyHandlerWrappers { handler.addIncludedMethods(httpMethod.name()); } if (compression.getExcludedUserAgents() != null) { - handler.setExcludedAgentPatterns(compression.getExcludedUserAgents()); + try { + handler.setExcludedAgentPatterns(compression.getExcludedUserAgents()); + } + catch (NoSuchMethodError ex) { + // Jetty 10 does not support User-Agent-based exclusions + } } return handler; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java index d1fe7c98cd..afb02094f6 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.springframework.boot.web.embedded.jetty; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URL; @@ -70,6 +71,7 @@ import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.ResourceLoader; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; /** @@ -306,7 +308,16 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor holder.setInitParameter("dirAllowed", "false"); holder.setInitOrder(1); 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); + } } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java index b027e1c2f4..093fb69867 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyWebServer.java @@ -32,7 +32,6 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerWrapper; 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.GracefulShutdownResult; @@ -120,18 +119,7 @@ public class JettyWebServer implements WebServer { // Cache the connectors and then remove them to prevent requests being // handled before the application context is ready. 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 this.server.start(); this.server.setStopAtShutdown(false); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java index 9d02b64cea..d9f57b33ff 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/SslServerCustomizer.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -106,8 +106,22 @@ class SslServerCustomizer implements JettyServerCustomizer { private ServerConnector createHttp11ServerConnector(Server server, HttpConfiguration config, SslContextFactory.Server sslContextFactory) { HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config); - SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, - HttpVersion.HTTP_1_1.asString()); + SslConnectionFactory sslConnectionFactory; + 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, connectionFactory); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/Jetty10ServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/Jetty10ServletWebServerFactoryTests.java new file mode 100644 index 0000000000..97a95a4e79 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/Jetty10ServletWebServerFactoryTests.java @@ -0,0 +1,57 @@ +/* + * 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 { + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java index 44a02c996a..f71ee30aef 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java @@ -1,5 +1,5 @@ /* - * 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"); * you may not use this file except in compliance with the License. @@ -16,11 +16,13 @@ package org.springframework.boot.web.embedded.jetty; +import java.lang.reflect.Method; import java.net.InetAddress; import java.time.Duration; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EventListener; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; @@ -46,6 +48,7 @@ import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerWrapper; 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.ThreadPool; import org.eclipse.jetty.webapp.AbstractConfiguration; @@ -59,6 +62,7 @@ import org.springframework.boot.web.server.Shutdown; import org.springframework.boot.web.server.Ssl; import org.springframework.boot.web.server.WebServerException; 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.assertThatExceptionOfType; @@ -77,7 +81,7 @@ import static org.mockito.Mockito.mock; class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFactoryTests { @Test - void correctVersionOfJettyUsed() { + protected void correctVersionOfJettyUsed() { String jettyVersion = ErrorHandler.class.getPackage().getImplementationVersion(); Matcher matcher = Pattern.compile("[0-9]+.[0-9]+.([0-9]+)[\\.-].*").matcher(jettyVersion); assertThat(matcher.find()).isTrue(); @@ -85,7 +89,7 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac } @Test - void jettyConfigurations() throws Exception { + protected void jettyConfigurations() throws Exception { JettyServletWebServerFactory factory = getFactory(); Configuration[] configurations = new Configuration[4]; Arrays.setAll(configurations, (i) -> mock(Configuration.class)); @@ -143,9 +147,9 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac JettyWebServer jettyWebServer = (JettyWebServer) this.webServer; ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0]; SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class); - assertThat(connectionFactory.getSslContextFactory().getIncludeCipherSuites()).containsExactly("ALPHA", "BRAVO", - "CHARLIE"); - assertThat(connectionFactory.getSslContextFactory().getExcludeCipherSuites()).isEmpty(); + SslContextFactory sslContextFactory = extractSslContextFactory(connectionFactory); + assertThat(sslContextFactory.getIncludeCipherSuites()).containsExactly("ALPHA", "BRAVO", "CHARLIE"); + assertThat(sslContextFactory.getExcludeCipherSuites()).isEmpty(); } @Test @@ -166,8 +170,8 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac JettyWebServer jettyWebServer = (JettyWebServer) this.webServer; ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0]; SslConnectionFactory connectionFactory = connector.getConnectionFactory(SslConnectionFactory.class); - assertThat(connectionFactory.getSslContextFactory().getIncludeProtocols()).containsExactly("TLSv1.1", - "TLSv1.2"); + SslContextFactory sslContextFactory = extractSslContextFactory(connectionFactory); + assertThat(sslContextFactory.getIncludeProtocols()).containsExactly("TLSv1.1", "TLSv1.2"); } @Test @@ -179,7 +183,19 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac JettyWebServer jettyWebServer = (JettyWebServer) this.webServer; ServerConnector connector = (ServerConnector) jettyWebServer.getServer().getConnectors()[0]; 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 @@ -372,7 +388,7 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac JettyServletWebServerFactory factory = getFactory(); factory.addServerCustomizers((JettyServerCustomizer) (server) -> { Collection contexts = server.getBeans(WebAppContext.class); - contexts.iterator().next().addEventListener(new ServletContextListener() { + EventListener eventListener = new ServletContextListener() { @Override public void contextInitialized(ServletContextEvent event) { @@ -382,8 +398,17 @@ class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFac @Override 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(() -> { JettyWebServer jettyWebServer = (JettyWebServer) factory.getWebServer(); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java index 3b6b073491..3adfff4c1d 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java @@ -842,7 +842,7 @@ public abstract class AbstractServletWebServerFactoryTests { } @Test - void noCompressionForUserAgent() throws Exception { + protected void noCompressionForUserAgent() throws Exception { assertThat(doTestCompression(10000, null, new String[] { "testUserAgent" })).isFalse(); }