From ed5d16de8408be54b19134e00f24fb1bd2ab9d85 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 16 Jan 2023 11:11:45 +0000 Subject: [PATCH] Upgrade to Jetty 12 Closes gh-36073 --- .../build.gradle | 4 +- .../JettyMetricsAutoConfigurationTests.java | 2 - .../spring-boot-autoconfigure/build.gradle | 16 +- ...verFactoryCustomizerAutoConfiguration.java | 2 +- .../JettyWebServerFactoryCustomizer.java | 29 +- ...ReactiveWebServerFactoryConfiguration.java | 12 +- .../ClientHttpConnectorAutoConfiguration.java | 1 - ...ientHttpConnectorFactoryConfiguration.java | 19 -- .../JettyClientHttpConnectorFactory.java | 64 ---- .../ServletWebServerFactoryConfiguration.java | 2 +- ...yWebSocketReactiveWebServerCustomizer.java | 20 +- .../WebSocketReactiveAutoConfiguration.java | 2 +- ...tyWebSocketServletWebServerCustomizer.java | 20 +- .../WebSocketServletAutoConfiguration.java | 4 +- .../jta/JtaAutoConfigurationTests.java | 2 + .../web/ServerPropertiesTests.java | 78 +---- .../JettyWebServerFactoryCustomizerTests.java | 2 - ...ebServerFactoryAutoConfigurationTests.java | 3 - ...ntHttpConnectorAutoConfigurationTests.java | 25 +- ...ttpConnectorFactoryConfigurationTests.java | 43 --- .../JettyClientHttpConnectorFactoryTests.java | 34 --- .../MultipartAutoConfigurationTests.java | 3 - ...ebServerFactoryAutoConfigurationTests.java | 3 - ...tWebServerServletContextListenerTests.java | 2 - ...bSocketReactiveAutoConfigurationTests.java | 4 +- ...ebSocketServletAutoConfigurationTests.java | 4 +- .../spring-boot-dependencies/build.gradle | 7 +- .../spring-boot-devtools/build.gradle | 4 +- .../getting-started/system-requirements.adoc | 4 +- .../spring-boot-starter-jetty/build.gradle | 16 +- .../servlet/Servlet5ClassPathOverrides.java | 41 --- spring-boot-project/spring-boot/build.gradle | 20 +- .../client/ClientHttpRequestFactories.java | 2 +- .../web/embedded/jetty/GracefulShutdown.java | 1 + .../web/embedded/jetty/JasperInitializer.java | 16 +- .../jetty/JettyEmbeddedErrorHandler.java | 24 +- .../jetty/JettyEmbeddedWebAppContext.java | 12 +- .../embedded/jetty/JettyHandlerWrappers.java | 30 +- .../jetty/JettyReactiveWebServerFactory.java | 9 +- .../jetty/JettyServletWebServerFactory.java | 275 ++++++++++++------ .../web/embedded/jetty/JettyWebServer.java | 34 ++- ...ervletContextInitializerConfiguration.java | 22 +- .../embedded/jetty/SslServerCustomizer.java | 14 +- .../JettyReactiveWebServerFactoryTests.java | 20 +- .../JettyServletWebServerFactoryTests.java | 46 +-- ...AbstractReactiveWebServerFactoryTests.java | 6 +- .../ServletComponentScanIntegrationTests.java | 4 +- .../ServletWebServerMvcIntegrationTests.java | 3 - .../AbstractServletWebServerFactoryTests.java | 19 +- .../spring-boot-server-tests-app/build.gradle | 10 +- .../build.gradle | 9 +- .../src/main/resources/application.properties | 3 +- .../build.gradle | 4 - .../spring-boot-smoke-test-jetty/build.gradle | 4 - .../src/main/resources/application.properties | 2 +- .../jetty/SampleJettyApplicationTests.java | 7 +- .../build.gradle | 4 - 57 files changed, 377 insertions(+), 695 deletions(-) delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactoryTests.java delete mode 100644 spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/Servlet5ClassPathOverrides.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle index 777cab24fef..6992d418509 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/build.gradle @@ -161,9 +161,7 @@ dependencies { testImplementation("org.assertj:assertj-core") testImplementation("org.awaitility:awaitility") testImplementation("org.cache2k:cache2k-api") - testImplementation("org.eclipse.jetty:jetty-webapp") { - exclude group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api" - } + testImplementation("org.eclipse.jetty.ee10:jetty-ee10-webapp") testImplementation("org.glassfish.jersey.ext:jersey-spring6") testImplementation("org.glassfish.jersey.media:jersey-media-json-jackson") testImplementation("org.hamcrest:hamcrest") diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jetty/JettyMetricsAutoConfigurationTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jetty/JettyMetricsAutoConfigurationTests.java index d53632c5441..0e8437a192b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jetty/JettyMetricsAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/web/jetty/JettyMetricsAutoConfigurationTests.java @@ -31,7 +31,6 @@ import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactor import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext; @@ -50,7 +49,6 @@ import static org.mockito.Mockito.mock; * @author Andy Wilkinson * @author Chris Bono */ -@Servlet5ClassPathOverrides class JettyMetricsAutoConfigurationTests { @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/build.gradle b/spring-boot-project/spring-boot-autoconfigure/build.gradle index b48da7b7bf4..ed61c0213dd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-autoconfigure/build.gradle @@ -78,20 +78,10 @@ dependencies { optional("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect") optional("org.aspectj:aspectjweaver") optional("org.cache2k:cache2k-spring") - optional("org.eclipse.jetty:jetty-webapp") { - exclude(group: "org.eclipse.jetty", module: "jetty-jndi") - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - } + optional("org.eclipse.jetty.ee10:jetty-ee10-webapp") optional("org.eclipse.jetty:jetty-reactive-httpclient") - optional("org.eclipse.jetty.websocket:websocket-jakarta-server") { - exclude(group: "org.eclipse.jetty", module: "jetty-jndi") - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-websocket-api") - } - optional("org.eclipse.jetty.websocket:websocket-jetty-server") { - exclude(group: "org.eclipse.jetty", module: "jetty-jndi") - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - } + optional("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server") + optional("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server") optional("org.ehcache:ehcache") { artifact { classifier = 'jakarta' diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java index eb7be8fe2ff..371ab503452 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java @@ -19,9 +19,9 @@ package org.springframework.boot.autoconfigure.web.embedded; import io.undertow.Undertow; import org.apache.catalina.startup.Tomcat; import org.apache.coyote.UpgradeProtocol; +import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.Loader; -import org.eclipse.jetty.webapp.WebAppContext; import org.xnio.SslClientAuthMode; import reactor.netty.http.server.HttpServer; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java index e53118c5d9a..c12333dc9cf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java @@ -18,7 +18,9 @@ package org.springframework.boot.autoconfigure.web.embedded; import java.time.Duration; import java.util.Arrays; +import java.util.List; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.server.CustomRequestLog; @@ -26,9 +28,6 @@ import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.RequestLogWriter; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.server.handler.HandlerCollection; -import org.eclipse.jetty.server.handler.HandlerWrapper; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.cloud.CloudPlatform; @@ -131,17 +130,21 @@ public class JettyWebServerFactoryCustomizer setHandlerMaxHttpFormPostSize(server.getHandlers()); } - private void setHandlerMaxHttpFormPostSize(Handler... handlers) { + private void setHandlerMaxHttpFormPostSize(List handlers) { for (Handler handler : handlers) { - if (handler instanceof ContextHandler contextHandler) { - contextHandler.setMaxFormContentSize(maxHttpFormPostSize); - } - else if (handler instanceof HandlerWrapper wrapper) { - setHandlerMaxHttpFormPostSize(wrapper.getHandler()); - } - else if (handler instanceof HandlerCollection collection) { - setHandlerMaxHttpFormPostSize(collection.getHandlers()); - } + setHandlerMaxHttpFormPostSize(handler); + } + } + + private void setHandlerMaxHttpFormPostSize(Handler handler) { + if (handler instanceof ServletContextHandler contextHandler) { + contextHandler.setMaxFormContentSize(maxHttpFormPostSize); + } + else if (handler instanceof Handler.Wrapper wrapper) { + setHandlerMaxHttpFormPostSize(wrapper.getHandler()); + } + else if (handler instanceof Handler.Collection collection) { + setHandlerMaxHttpFormPostSize(collection.getHandlers()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java index 2d92ced3d6e..8752f5025fc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java @@ -17,7 +17,7 @@ package org.springframework.boot.autoconfigure.web.reactive; import io.undertow.Undertow; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import reactor.netty.http.server.HttpServer; import org.springframework.beans.factory.ObjectProvider; @@ -39,7 +39,6 @@ import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.http.client.reactive.JettyResourceFactory; import org.springframework.http.client.reactive.ReactorResourceFactory; /** @@ -97,17 +96,10 @@ abstract class ReactiveWebServerFactoryConfiguration { static class EmbeddedJetty { @Bean - @ConditionalOnMissingBean - JettyResourceFactory jettyServerResourceFactory() { - return new JettyResourceFactory(); - } - - @Bean - JettyReactiveWebServerFactory jettyReactiveWebServerFactory(JettyResourceFactory resourceFactory, + JettyReactiveWebServerFactory jettyReactiveWebServerFactory( ObjectProvider serverCustomizers) { JettyReactiveWebServerFactory serverFactory = new JettyReactiveWebServerFactory(); serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().toList()); - serverFactory.setResourceFactory(resourceFactory); return serverFactory; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java index 34bf67e605a..308ac80f503 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfiguration.java @@ -46,7 +46,6 @@ import org.springframework.web.reactive.function.client.WebClient; @ConditionalOnClass(WebClient.class) @AutoConfigureAfter(SslAutoConfiguration.class) @Import({ ClientHttpConnectorFactoryConfiguration.ReactorNetty.class, - ClientHttpConnectorFactoryConfiguration.JettyClient.class, ClientHttpConnectorFactoryConfiguration.HttpClient5.class, ClientHttpConnectorFactoryConfiguration.JdkClient.class }) public class ClientHttpConnectorAutoConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfiguration.java index cf0769bc0cd..e5ea5cca20b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfiguration.java @@ -27,7 +27,6 @@ import org.springframework.boot.autoconfigure.reactor.netty.ReactorNettyConfigur import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.http.client.reactive.JettyResourceFactory; import org.springframework.http.client.reactive.ReactorResourceFactory; /** @@ -55,24 +54,6 @@ class ClientHttpConnectorFactoryConfiguration { } - @Configuration(proxyBeanMethods = false) - @ConditionalOnClass(org.eclipse.jetty.reactive.client.ReactiveRequest.class) - @ConditionalOnMissingBean(ClientHttpConnectorFactory.class) - static class JettyClient { - - @Bean - @ConditionalOnMissingBean - JettyResourceFactory jettyClientResourceFactory() { - return new JettyResourceFactory(); - } - - @Bean - JettyClientHttpConnectorFactory jettyClientHttpConnectorFactory(JettyResourceFactory jettyResourceFactory) { - return new JettyClientHttpConnectorFactory(jettyResourceFactory); - } - - } - @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ HttpAsyncClients.class, AsyncRequestProducer.class, ReactiveResponseConsumer.class }) @ConditionalOnMissingBean(ClientHttpConnectorFactory.class) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java deleted file mode 100644 index 5824abf1ca9..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactory.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2012-2023 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.autoconfigure.web.reactive.function.client; - -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; -import org.eclipse.jetty.io.ClientConnector; -import org.eclipse.jetty.util.ssl.SslContextFactory; - -import org.springframework.boot.ssl.SslBundle; -import org.springframework.boot.ssl.SslOptions; -import org.springframework.http.client.reactive.JettyClientHttpConnector; -import org.springframework.http.client.reactive.JettyResourceFactory; - -/** - * {@link ClientHttpConnectorFactory} for {@link JettyClientHttpConnector}. - * - * @author Phillip Webb - */ -class JettyClientHttpConnectorFactory implements ClientHttpConnectorFactory { - - private final JettyResourceFactory jettyResourceFactory; - - JettyClientHttpConnectorFactory(JettyResourceFactory jettyResourceFactory) { - this.jettyResourceFactory = jettyResourceFactory; - } - - @Override - public JettyClientHttpConnector createClientHttpConnector(SslBundle sslBundle) { - SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); - if (sslBundle != null) { - SslOptions options = sslBundle.getOptions(); - if (options.getCiphers() != null) { - sslContextFactory.setIncludeCipherSuites(options.getCiphers()); - sslContextFactory.setExcludeCipherSuites(); - } - if (options.getEnabledProtocols() != null) { - sslContextFactory.setIncludeProtocols(options.getEnabledProtocols()); - sslContextFactory.setExcludeProtocols(); - } - sslContextFactory.setSslContext(sslBundle.createSslContext()); - } - ClientConnector connector = new ClientConnector(); - connector.setSslContextFactory(sslContextFactory); - HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(connector); - HttpClient httpClient = new HttpClient(transport); - return new JettyClientHttpConnector(httpClient, this.jettyResourceFactory); - } - -} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java index b33e8eaf4ce..266b298eeb2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java @@ -20,9 +20,9 @@ import io.undertow.Undertow; import jakarta.servlet.Servlet; import org.apache.catalina.startup.Tomcat; import org.apache.coyote.UpgradeProtocol; +import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.Loader; -import org.eclipse.jetty.webapp.WebAppContext; import org.xnio.SslClientAuthMode; import org.springframework.beans.factory.ObjectProvider; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/JettyWebSocketReactiveWebServerCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/JettyWebSocketReactiveWebServerCustomizer.java index 4c050c4237e..00ca570b9d6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/JettyWebSocketReactiveWebServerCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/JettyWebSocketReactiveWebServerCustomizer.java @@ -17,15 +17,13 @@ package org.springframework.boot.autoconfigure.websocket.reactive; import jakarta.servlet.ServletContext; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.websocket.jakarta.server.JakartaWebSocketServerContainer; +import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer; +import org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter; import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.handler.HandlerCollection; -import org.eclipse.jetty.server.handler.HandlerWrapper; -import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.websocket.core.server.WebSocketMappings; import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents; -import org.eclipse.jetty.websocket.jakarta.server.internal.JakartaWebSocketServerContainer; -import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; -import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter; import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; @@ -47,13 +45,13 @@ public class JettyWebSocketReactiveWebServerCustomizer if (servletContextHandler != null) { ServletContext servletContext = servletContextHandler.getServletContext(); if (JettyWebSocketServerContainer.getContainer(servletContext) == null) { - WebSocketServerComponents.ensureWebSocketComponents(server, servletContext); + WebSocketServerComponents.ensureWebSocketComponents(server, servletContextHandler); JettyWebSocketServerContainer.ensureContainer(servletContext); } if (JakartaWebSocketServerContainer.getContainer(servletContext) == null) { - WebSocketServerComponents.ensureWebSocketComponents(server, servletContext); + WebSocketServerComponents.ensureWebSocketComponents(server, servletContextHandler); WebSocketUpgradeFilter.ensureFilter(servletContext); - WebSocketMappings.ensureMappings(servletContext); + WebSocketMappings.ensureMappings(servletContextHandler); JakartaWebSocketServerContainer.ensureContainer(servletContext); } } @@ -64,10 +62,10 @@ public class JettyWebSocketReactiveWebServerCustomizer if (handler instanceof ServletContextHandler servletContextHandler) { return servletContextHandler; } - if (handler instanceof HandlerWrapper handlerWrapper) { + if (handler instanceof Handler.Wrapper handlerWrapper) { return findServletContextHandler(handlerWrapper.getHandler()); } - if (handler instanceof HandlerCollection handlerCollection) { + if (handler instanceof Handler.Collection handlerCollection) { for (Handler contained : handlerCollection.getHandlers()) { ServletContextHandler servletContextHandler = findServletContextHandler(contained); if (servletContextHandler != null) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java index 2f26b169858..3592ba14b3c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfiguration.java @@ -20,7 +20,7 @@ import jakarta.servlet.Servlet; import jakarta.websocket.server.ServerContainer; import org.apache.catalina.startup.Tomcat; import org.apache.tomcat.websocket.server.WsSci; -import org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; +import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/JettyWebSocketServletWebServerCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/JettyWebSocketServletWebServerCustomizer.java index 8ee41bc966c..ccf0ef8f379 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/JettyWebSocketServletWebServerCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/JettyWebSocketServletWebServerCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2023 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,13 +16,13 @@ package org.springframework.boot.autoconfigure.websocket.servlet; -import org.eclipse.jetty.webapp.AbstractConfiguration; -import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.websocket.jakarta.server.JakartaWebSocketServerContainer; +import org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer; +import org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter; import org.eclipse.jetty.websocket.core.server.WebSocketMappings; import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents; -import org.eclipse.jetty.websocket.jakarta.server.internal.JakartaWebSocketServerContainer; -import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; -import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; @@ -41,20 +41,20 @@ public class JettyWebSocketServletWebServerCustomizer @Override public void customize(JettyServletWebServerFactory factory) { - factory.addConfigurations(new AbstractConfiguration() { + factory.addConfigurations(new AbstractConfiguration(new AbstractConfiguration.Builder()) { @Override public void configure(WebAppContext context) throws Exception { if (JettyWebSocketServerContainer.getContainer(context.getServletContext()) == null) { WebSocketServerComponents.ensureWebSocketComponents(context.getServer(), - context.getServletContext()); + context.getContext().getContextHandler()); JettyWebSocketServerContainer.ensureContainer(context.getServletContext()); } if (JakartaWebSocketServerContainer.getContainer(context.getServletContext()) == null) { WebSocketServerComponents.ensureWebSocketComponents(context.getServer(), - context.getServletContext()); + context.getContext().getContextHandler()); WebSocketUpgradeFilter.ensureFilter(context.getServletContext()); - WebSocketMappings.ensureMappings(context.getServletContext()); + WebSocketMappings.ensureMappings(context.getContext().getContextHandler()); JakartaWebSocketServerContainer.ensureContainer(context.getServletContext()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.java index 48d3379ef3f..a84c02eb1af 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.java @@ -23,8 +23,8 @@ import jakarta.servlet.Servlet; import jakarta.websocket.server.ServerContainer; import org.apache.catalina.startup.Tomcat; import org.apache.tomcat.websocket.server.WsSci; -import org.eclipse.jetty.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; -import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter; +import org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer; +import org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java index e70fb24e477..a2e0647780c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/transaction/jta/JtaAutoConfigurationTests.java @@ -43,6 +43,7 @@ import org.osjava.sj.loader.JndiLoader; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.boot.testsupport.classpath.ClassPathExclusions; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -62,6 +63,7 @@ import static org.mockito.Mockito.mock; * @author Kazuki Shimizu * @author Nishant Raut */ +@ClassPathExclusions("jetty-jndi-*.jar") class JtaAutoConfigurationTests { private AnnotationConfigApplicationContext context; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 07b26273054..ff8b377d2d9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -16,21 +16,14 @@ package org.springframework.boot.autoconfigure.web; -import java.io.IOException; import java.net.InetAddress; -import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; import io.undertow.UndertowOptions; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import org.apache.catalina.connector.Connector; import org.apache.catalina.core.StandardContext; import org.apache.catalina.core.StandardEngine; @@ -38,8 +31,7 @@ import org.apache.catalina.valves.AccessLogValve; import org.apache.catalina.valves.RemoteIpValve; import org.apache.coyote.AbstractProtocol; import org.apache.tomcat.util.net.AbstractEndpoint; -import org.eclipse.jetty.server.HttpChannel; -import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.Test; @@ -51,21 +43,11 @@ import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySource; import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyWebServer; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.servlet.ServletContextInitializer; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.client.ClientHttpResponse; import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import org.springframework.util.unit.DataSize; -import org.springframework.web.client.ResponseErrorHandler; -import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; @@ -444,7 +426,6 @@ class ServerPropertiesTests { } @Test - @Servlet5ClassPathOverrides void jettyThreadPoolPropertyDefaultsShouldMatchServerDefault() { JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0); JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer(); @@ -459,61 +440,12 @@ class ServerPropertiesTests { } @Test - @Servlet5ClassPathOverrides void jettyMaxHttpFormPostSizeMatchesDefault() { JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0); - JettyWebServer jetty = (JettyWebServer) jettyFactory - .getWebServer((ServletContextInitializer) (servletContext) -> servletContext - .addServlet("formPost", new HttpServlet() { - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - req.getParameterMap(); - } - - }) - .addMapping("/form")); - jetty.start(); - org.eclipse.jetty.server.Connector connector = jetty.getServer().getConnectors()[0]; - final AtomicReference failure = new AtomicReference<>(); - connector.addBean(new HttpChannel.Listener() { - - @Override - public void onDispatchFailure(Request request, Throwable ex) { - failure.set(ex); - } - - }); - try { - RestTemplate template = new RestTemplate(); - template.setErrorHandler(new ResponseErrorHandler() { - - @Override - public boolean hasError(ClientHttpResponse response) throws IOException { - return false; - } - - @Override - public void handleError(ClientHttpResponse response) throws IOException { - - } - - }); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap body = new LinkedMultiValueMap<>(); - body.add("data", "a".repeat(250000)); - HttpEntity> entity = new HttpEntity<>(body, headers); - template.postForEntity(URI.create("http://localhost:" + jetty.getPort() + "/form"), entity, Void.class); - assertThat(failure.get()).isNotNull(); - String message = failure.get().getCause().getMessage(); - int defaultMaxPostSize = Integer.parseInt(message.substring(message.lastIndexOf(' ')).trim()); - assertThat(this.properties.getJetty().getMaxHttpFormPostSize().toBytes()).isEqualTo(defaultMaxPostSize); - } - finally { - jetty.stop(); - } + JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer(); + Server server = jetty.getServer(); + assertThat(this.properties.getJetty().getMaxHttpFormPostSize().toBytes()) + .isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormContentSize()); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java index a4367e15064..eef94bb88a2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java @@ -47,7 +47,6 @@ import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyWebServer; @@ -67,7 +66,6 @@ import static org.mockito.Mockito.mock; * @author HaiTao Zhang */ @DirtiesUrlFactories -@Servlet5ClassPathOverrides class JettyWebServerFactoryCustomizerTests { private MockEnvironment environment; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java index 7b1b38cf267..044c1bfff9f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java @@ -31,7 +31,6 @@ import org.springframework.boot.ssl.NoSuchSslBundleException; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory; @@ -231,7 +230,6 @@ class ReactiveWebServerFactoryAutoConfigurationTests { } @Test - @Servlet5ClassPathOverrides void jettyServerCustomizerBeanIsAddedToFactory() { new ReactiveWebApplicationContextRunner(AnnotationConfigReactiveWebApplicationContext::new) .withConfiguration(AutoConfigurations.of(ReactiveWebServerFactoryAutoConfiguration.class)) @@ -244,7 +242,6 @@ class ReactiveWebServerFactoryAutoConfigurationTests { } @Test - @Servlet5ClassPathOverrides void jettyServerCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { new ReactiveWebApplicationContextRunner(AnnotationConfigReactiveWebServerApplicationContext::new) .withConfiguration(AutoConfigurations.of(ReactiveWebServerFactoryAutoConfiguration.class)) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java index 2a05496baf1..b3fe66cea5c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorAutoConfigurationTests.java @@ -17,7 +17,6 @@ package org.springframework.boot.autoconfigure.web.reactive.function.client; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; -import org.eclipse.jetty.reactive.client.ReactiveRequest; import org.junit.jupiter.api.Test; import reactor.netty.http.client.HttpClient; @@ -62,36 +61,20 @@ class ClientHttpConnectorAutoConfigurationTests { } @Test - void whenReactorIsUnavailableThenJettyBeansAreDefined() { + void whenReactorIsUnavailableThenHttpClientBeansAreDefined() { this.contextRunner.withClassLoader(new FilteredClassLoader(HttpClient.class)).run((context) -> { BeanDefinition customizerDefinition = context.getBeanFactory() .getBeanDefinition("webClientHttpConnectorCustomizer"); assertThat(customizerDefinition.isLazyInit()).isTrue(); BeanDefinition connectorDefinition = context.getBeanFactory().getBeanDefinition("webClientHttpConnector"); assertThat(connectorDefinition.isLazyInit()).isTrue(); - assertThat(context).hasBean("jettyClientResourceFactory"); - assertThat(context).hasBean("jettyClientHttpConnectorFactory"); + assertThat(context).hasBean("httpComponentsClientHttpConnectorFactory"); }); } @Test - void whenReactorAndJettyAreUnavailableThenHttpClientBeansAreDefined() { - this.contextRunner.withClassLoader(new FilteredClassLoader(HttpClient.class, ReactiveRequest.class)) - .run((context) -> { - BeanDefinition customizerDefinition = context.getBeanFactory() - .getBeanDefinition("webClientHttpConnectorCustomizer"); - assertThat(customizerDefinition.isLazyInit()).isTrue(); - BeanDefinition connectorDefinition = context.getBeanFactory() - .getBeanDefinition("webClientHttpConnector"); - assertThat(connectorDefinition.isLazyInit()).isTrue(); - assertThat(context).hasBean("httpComponentsClientHttpConnectorFactory"); - }); - } - - @Test - void whenReactorJettyAndHttpClientBeansAreUnavailableThenJdkClientBeansAreDefined() { - this.contextRunner - .withClassLoader(new FilteredClassLoader(HttpClient.class, ReactiveRequest.class, HttpAsyncClients.class)) + void whenReactorAndHttpClientBeansAreUnavailableThenJdkClientBeansAreDefined() { + this.contextRunner.withClassLoader(new FilteredClassLoader(HttpClient.class, HttpAsyncClients.class)) .run((context) -> { BeanDefinition customizerDefinition = context.getBeanFactory() .getBeanDefinition("webClientHttpConnectorCustomizer"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfigurationTests.java index 79991a079b6..5d7fd0fab66 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/ClientHttpConnectorFactoryConfigurationTests.java @@ -16,11 +16,6 @@ package org.springframework.boot.autoconfigure.web.reactive.function.client; -import java.util.concurrent.Executor; - -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.util.thread.Scheduler; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -32,13 +27,9 @@ import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.http.client.reactive.HttpComponentsClientHttpConnector; -import org.springframework.http.client.reactive.JettyClientHttpConnector; -import org.springframework.http.client.reactive.JettyResourceFactory; -import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.then; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; /** @@ -50,40 +41,6 @@ import static org.mockito.Mockito.spy; */ class ClientHttpConnectorFactoryConfigurationTests { - @Test - void jettyClientHttpConnectorAppliesJettyResourceFactory() { - Executor executor = mock(Executor.class); - ByteBufferPool byteBufferPool = mock(ByteBufferPool.class); - Scheduler scheduler = mock(Scheduler.class); - JettyResourceFactory jettyResourceFactory = new JettyResourceFactory(); - jettyResourceFactory.setExecutor(executor); - jettyResourceFactory.setByteBufferPool(byteBufferPool); - jettyResourceFactory.setScheduler(scheduler); - JettyClientHttpConnectorFactory connectorFactory = getJettyClientHttpConnectorFactory(jettyResourceFactory); - JettyClientHttpConnector connector = connectorFactory.createClientHttpConnector(); - HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(connector, "httpClient"); - assertThat(httpClient.getExecutor()).isSameAs(executor); - assertThat(httpClient.getByteBufferPool()).isSameAs(byteBufferPool); - assertThat(httpClient.getScheduler()).isSameAs(scheduler); - } - - @Test - void JettyResourceFactoryHasSslContextFactory() { - // gh-16810 - JettyResourceFactory jettyResourceFactory = new JettyResourceFactory(); - JettyClientHttpConnectorFactory connectorFactory = getJettyClientHttpConnectorFactory(jettyResourceFactory); - JettyClientHttpConnector connector = connectorFactory.createClientHttpConnector(); - HttpClient httpClient = (HttpClient) ReflectionTestUtils.getField(connector, "httpClient"); - assertThat(httpClient.getSslContextFactory()).isNotNull(); - } - - private JettyClientHttpConnectorFactory getJettyClientHttpConnectorFactory( - JettyResourceFactory jettyResourceFactory) { - ClientHttpConnectorFactoryConfiguration.JettyClient jettyClient = new ClientHttpConnectorFactoryConfiguration.JettyClient(); - // We shouldn't usually call this method directly since it's on a non-proxy config - return ReflectionTestUtils.invokeMethod(jettyClient, "jettyClientHttpConnectorFactory", jettyResourceFactory); - } - @Test void shouldApplyHttpClientMapper() { JksSslStoreDetails storeDetails = JksSslStoreDetails.forLocation("classpath:test.jks"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactoryTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactoryTests.java deleted file mode 100644 index ad99f85a778..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/function/client/JettyClientHttpConnectorFactoryTests.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012-2023 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.autoconfigure.web.reactive.function.client; - -import org.springframework.http.client.reactive.JettyResourceFactory; - -/** - * Tests for {@link JettyClientHttpConnectorFactory}. - * - * @author Phillip Webb - */ -class JettyClientHttpConnectorFactoryTests extends AbstractClientHttpConnectorFactoryTests { - - @Override - protected ClientHttpConnectorFactory getFactory() { - JettyResourceFactory resourceFactory = new JettyResourceFactory(); - return new JettyClientHttpConnectorFactory(resourceFactory); - } - -} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfigurationTests.java index b1e1dd0926a..f60ffd3a5ef 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfigurationTests.java @@ -31,7 +31,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.testsupport.classpath.ForkedClassPath; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; @@ -221,7 +220,6 @@ class MultipartAutoConfigurationTests { } - @Servlet5ClassPathOverrides @Configuration(proxyBeanMethods = false) static class WebServerWithNoMultipartJetty { @@ -282,7 +280,6 @@ class MultipartAutoConfigurationTests { } - @Servlet5ClassPathOverrides @Configuration(proxyBeanMethods = false) static class WebServerWithEverythingJetty { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java index 6555da75f8f..a3211bd3417 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfigurationTests.java @@ -38,7 +38,6 @@ import org.springframework.boot.test.context.assertj.AssertableWebApplicationCon import org.springframework.boot.test.context.runner.ContextConsumer; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; @@ -156,7 +155,6 @@ class ServletWebServerFactoryAutoConfigurationTests { } @Test - @Servlet5ClassPathOverrides void jettyServerCustomizerBeanIsAddedToFactory() { WebApplicationContextRunner runner = new WebApplicationContextRunner( AnnotationConfigServletWebServerApplicationContext::new) @@ -171,7 +169,6 @@ class ServletWebServerFactoryAutoConfigurationTests { } @Test - @Servlet5ClassPathOverrides void jettyServerCustomizerRegisteredAsBeanAndViaFactoryIsOnlyCalledOnce() { WebApplicationContextRunner runner = new WebApplicationContextRunner( AnnotationConfigServletWebServerApplicationContext::new) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerServletContextListenerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerServletContextListenerTests.java index d2ef77630aa..ec52ceab9cb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerServletContextListenerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerServletContextListenerTests.java @@ -26,7 +26,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.boot.testsupport.classpath.ForkedClassPath; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; @@ -90,7 +89,6 @@ class ServletWebServerServletContextListenerTests { } - @Servlet5ClassPathOverrides @Configuration(proxyBeanMethods = false) static class JettyConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfigurationTests.java index e9505d09632..e092a9262f2 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/reactive/WebSocketReactiveAutoConfigurationTests.java @@ -24,14 +24,13 @@ import jakarta.websocket.server.ServerContainer; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.startup.Tomcat; -import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.boot.testsupport.classpath.ForkedClassPath; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyWebServer; import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; @@ -123,7 +122,6 @@ class WebSocketReactiveAutoConfigurationTests { } - @Servlet5ClassPathOverrides @Configuration(proxyBeanMethods = false) static class JettyConfiguration extends CommonConfiguration { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java index 423c89df1d5..5bef88c48dd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfigurationTests.java @@ -30,7 +30,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.websocket.DeploymentException; import jakarta.websocket.server.ServerContainer; import jakarta.websocket.server.ServerEndpoint; -import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter; +import org.eclipse.jetty.ee10.websocket.servlet.WebSocketUpgradeFilter; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -42,7 +42,6 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.testsupport.classpath.ForkedClassPath; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.WebServer; @@ -182,7 +181,6 @@ class WebSocketServletAutoConfigurationTests { } - @Servlet5ClassPathOverrides @Configuration(proxyBeanMethods = false) static class JettyConfiguration extends CommonConfiguration { diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index eef9a4f5c9f..3e1c8aa7748 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -675,7 +675,12 @@ bom { ] } } - library("Jetty", "11.0.15") { + library("Jetty", "12.0.1") { + group("org.eclipse.jetty.ee10") { + imports = [ + "jetty-ee10-bom" + ] + } group("org.eclipse.jetty") { imports = [ "jetty-bom" diff --git a/spring-boot-project/spring-boot-devtools/build.gradle b/spring-boot-project/spring-boot-devtools/build.gradle index 7e33304df02..b3047fd5d52 100644 --- a/spring-boot-project/spring-boot-devtools/build.gradle +++ b/spring-boot-project/spring-boot-devtools/build.gradle @@ -65,9 +65,7 @@ dependencies { testImplementation("org.apache.tomcat.embed:tomcat-embed-jasper") testImplementation("org.assertj:assertj-core") testImplementation("org.awaitility:awaitility") - testImplementation("org.eclipse.jetty.websocket:websocket-jakarta-client") { - exclude group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-websocket-api" - } + testImplementation("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-client") testImplementation("org.hamcrest:hamcrest-library") testImplementation("org.hsqldb:hsqldb") testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/system-requirements.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/system-requirements.adoc index a180e0efb57..aa634dfb7d8 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/system-requirements.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/getting-started/system-requirements.adoc @@ -27,8 +27,8 @@ Spring Boot supports the following embedded servlet containers: | Tomcat 10.1 | 6.0 -| Jetty 11.0 -| 5.0 +| Jetty 12.0 +| 6.0 | Undertow 2.3 | 6.0 diff --git a/spring-boot-project/spring-boot-starters/spring-boot-starter-jetty/build.gradle b/spring-boot-project/spring-boot-starters/spring-boot-starter-jetty/build.gradle index eb83be39310..3050b1cd5c9 100644 --- a/spring-boot-project/spring-boot-starters/spring-boot-starter-jetty/build.gradle +++ b/spring-boot-project/spring-boot-starters/spring-boot-starter-jetty/build.gradle @@ -9,16 +9,8 @@ dependencies { api("jakarta.websocket:jakarta.websocket-api") api("jakarta.websocket:jakarta.websocket-client-api") api("org.apache.tomcat.embed:tomcat-embed-el") - api("org.eclipse.jetty:jetty-servlets") - api("org.eclipse.jetty:jetty-webapp") { - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - } - api("org.eclipse.jetty.websocket:websocket-jakarta-server") { - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-websocket-api") - } - api("org.eclipse.jetty.websocket:websocket-jetty-server") { - exclude group: "org.eclipse.jetty", module: "jetty-jndi" - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - } + api("org.eclipse.jetty.ee10:jetty-ee10-servlets") + api("org.eclipse.jetty.ee10:jetty-ee10-webapp") + api("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server") + api("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server") } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/Servlet5ClassPathOverrides.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/Servlet5ClassPathOverrides.java deleted file mode 100644 index 2fc8544d79a..00000000000 --- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/web/servlet/Servlet5ClassPathOverrides.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012-2022 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.testsupport.web.servlet; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.boot.testsupport.classpath.ClassPathExclusions; -import org.springframework.boot.testsupport.classpath.ClassPathOverrides; - -/** - * Annotation to downgrade to Servlet 5.0. - * - * @author Phillip Webb - * @since 3.0.0 - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Documented -@ClassPathExclusions("jakarta.servlet-api-6*.jar") -@ClassPathOverrides("jakarta.servlet:jakarta.servlet-api:5.0.0") -public @interface Servlet5ClassPathOverrides { - -} diff --git a/spring-boot-project/spring-boot/build.gradle b/spring-boot-project/spring-boot/build.gradle index 6b86fd02adc..ebc27fffe71 100644 --- a/spring-boot-project/spring-boot/build.gradle +++ b/spring-boot-project/spring-boot/build.gradle @@ -56,18 +56,12 @@ dependencies { optional("org.assertj:assertj-core") optional("org.apache.groovy:groovy") optional("org.apache.groovy:groovy-xml") - optional("org.eclipse.jetty:jetty-servlets") - optional("org.eclipse.jetty:jetty-util") - optional("org.eclipse.jetty:jetty-webapp") { - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - } - optional("org.eclipse.jetty:jetty-alpn-conscrypt-server") { - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - } - optional("org.eclipse.jetty.http2:http2-server") { - exclude(group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api") - } + optional("org.eclipse.jetty:jetty-alpn-conscrypt-server") optional("org.eclipse.jetty:jetty-client") + optional("org.eclipse.jetty:jetty-util") + optional("org.eclipse.jetty.ee10:jetty-ee10-servlets") + optional("org.eclipse.jetty.ee10:jetty-ee10-webapp") + optional("org.eclipse.jetty.http2:jetty-http2-server") optional("org.flywaydb:flyway-core") optional("org.hamcrest:hamcrest-library") optional("org.hibernate.orm:hibernate-core") @@ -120,8 +114,8 @@ dependencies { testImplementation("org.awaitility:awaitility") testImplementation("org.codehaus.janino:janino") testImplementation("org.eclipse.jetty:jetty-client") - testImplementation("org.eclipse.jetty.http2:http2-client") - testImplementation("org.eclipse.jetty.http2:http2-http-client-transport") + testImplementation("org.eclipse.jetty.http2:jetty-http2-client") + testImplementation("org.eclipse.jetty.http2:jetty-http2-client-transport") testImplementation("org.firebirdsql.jdbc:jaybird") { exclude group: "javax.resource", module: "connector-api" } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java index 0ecdc850a0b..9f4aa447548 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactories.java @@ -39,7 +39,7 @@ import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuil import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier; import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; import org.apache.hc.core5.http.io.SocketConfig; -import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic; +import org.eclipse.jetty.client.transport.HttpClientTransportDynamic; import org.eclipse.jetty.io.ClientConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; 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 dc2580d25ea..8ccd5d26e56 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 @@ -99,6 +99,7 @@ final class GracefulShutdown { while (this.shuttingDown && this.activeRequests.get() > 0) { sleep(100); } + System.out.println(this.activeRequests.get()); this.shuttingDown = false; long activeRequests = this.activeRequests.get(); if (activeRequests == 0) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JasperInitializer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JasperInitializer.java index 74b6b5a7561..d370f56d9fd 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JasperInitializer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JasperInitializer.java @@ -24,8 +24,8 @@ import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; import jakarta.servlet.ServletContainerInitializer; +import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.util.component.AbstractLifeCycle; -import org.eclipse.jetty.webapp.WebAppContext; import org.springframework.util.ClassUtils; @@ -63,7 +63,6 @@ class JasperInitializer extends AbstractLifeCycle { } @Override - @SuppressWarnings("deprecation") protected void doStart() throws Exception { if (this.initializer == null) { return; @@ -84,11 +83,11 @@ class JasperInitializer extends AbstractLifeCycle { try { Thread.currentThread().setContextClassLoader(this.context.getClassLoader()); try { - setExtendedListenerTypes(true); + this.context.getContext().setExtendedListenerTypes(true); this.initializer.onStartup(null, this.context.getServletContext()); } finally { - setExtendedListenerTypes(false); + this.context.getContext().setExtendedListenerTypes(false); } } finally { @@ -96,15 +95,6 @@ class JasperInitializer extends AbstractLifeCycle { } } - private void setExtendedListenerTypes(boolean extended) { - try { - this.context.getServletContext().setExtendedListenerTypes(extended); - } - catch (NoSuchMethodError ex) { - // Not available on Jetty 8 - } - } - /** * {@link URLStreamHandlerFactory} to support {@literal war} protocol. */ 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 5871fb668ac..924a5324213 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-2022 the original author or authors. + * Copyright 2012-2023 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,17 +16,8 @@ package org.springframework.boot.web.embedded.jetty; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.servlet.ErrorPageErrorHandler; /** * Variation of Jetty's {@link ErrorPageErrorHandler} that supports all {@link HttpMethod @@ -40,20 +31,9 @@ import org.eclipse.jetty.servlet.ErrorPageErrorHandler; */ class JettyEmbeddedErrorHandler extends ErrorPageErrorHandler { - private static final Set HANDLED_HTTP_METHODS = new HashSet<>(Arrays.asList("GET", "POST", "HEAD")); - @Override public boolean errorPageForMethod(String method) { return true; } - @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - if (!HANDLED_HTTP_METHODS.contains(baseRequest.getMethod())) { - baseRequest.setMethod("GET"); - } - super.handle(target, baseRequest, request, response); - } - } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedWebAppContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedWebAppContext.java index 000c0acd79c..d1caaf1d5a3 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedWebAppContext.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedWebAppContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2023 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,8 +16,9 @@ package org.springframework.boot.web.embedded.jetty; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.webapp.ClassMatcher; +import org.eclipse.jetty.ee10.webapp.WebAppContext; /** * Jetty {@link WebAppContext} used by {@link JettyWebServer} to support deferred @@ -27,6 +28,11 @@ import org.eclipse.jetty.webapp.WebAppContext; */ class JettyEmbeddedWebAppContext extends WebAppContext { + JettyEmbeddedWebAppContext() { + setServerClassMatcher(new ClassMatcher("org.springframework.boot.loader.")); + // setTempDirectory(WebInfConfiguration.getCanonicalNameForWebAppTmpDir(this)); + } + @Override protected ServletHandler newServletHandler() { return new JettyEmbeddedServletHandler(); 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 35a5286b81c..bb61f81c6b7 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-2022 the original author or authors. + * Copyright 2012-2023 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,15 +16,13 @@ package org.springframework.boot.web.embedded.jetty; -import java.io.IOException; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpFields.Mutable; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.handler.HandlerWrapper; +import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.handler.gzip.GzipHandler; +import org.eclipse.jetty.util.Callback; import org.springframework.boot.web.server.Compression; @@ -38,7 +36,7 @@ final class JettyHandlerWrappers { private JettyHandlerWrappers() { } - static HandlerWrapper createGzipHandlerWrapper(Compression compression) { + static Handler.Wrapper createGzipHandlerWrapper(Compression compression) { GzipHandler handler = new GzipHandler(); handler.setMinGzipSize((int) compression.getMinResponseSize().toBytes()); handler.setIncludedMimeTypes(compression.getMimeTypes()); @@ -48,14 +46,14 @@ final class JettyHandlerWrappers { return handler; } - static HandlerWrapper createServerHeaderHandlerWrapper(String header) { + static Handler.Wrapper createServerHeaderHandlerWrapper(String header) { return new ServerHeaderHandler(header); } /** - * {@link HandlerWrapper} to add a custom {@code server} header. + * {@link Handler.Wrapper} to add a custom {@code server} header. */ - private static class ServerHeaderHandler extends HandlerWrapper { + private static class ServerHeaderHandler extends Handler.Wrapper { private static final String SERVER_HEADER = "server"; @@ -66,12 +64,12 @@ final class JettyHandlerWrappers { } @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - if (!response.getHeaderNames().contains(SERVER_HEADER)) { - response.setHeader(SERVER_HEADER, this.value); + public boolean handle(Request request, Response response, Callback callback) throws Exception { + Mutable headers = response.getHeaders(); + if (!headers.contains(SERVER_HEADER)) { + headers.add(SERVER_HEADER, this.value); } - super.handle(target, baseRequest, request, response); + return super.handle(request, response, callback); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java index 3102dd4aa3d..61351492738 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactory.java @@ -26,6 +26,8 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.ConnectionFactory; @@ -35,10 +37,7 @@ import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.StatisticsHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.thread.ThreadPool; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; @@ -185,7 +184,7 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact server.setStopTimeout(0); ServletHolder servletHolder = new ServletHolder(servlet); servletHolder.setAsyncSupported(true); - ServletContextHandler contextHandler = new ServletContextHandler(server, "/", false, false); + ServletContextHandler contextHandler = new ServletContextHandler("/", false, false); contextHandler.addServlet(servletHolder, "/"); server.setHandler(addHandlerWrappers(contextHandler)); JettyReactiveWebServerFactory.logger.info("Server initialized with port: " + port); @@ -243,7 +242,7 @@ public class JettyReactiveWebServerFactory extends AbstractReactiveWebServerFact return handler; } - private Handler applyWrapper(Handler handler, HandlerWrapper wrapper) { + private Handler applyWrapper(Handler handler, Handler.Wrapper wrapper) { wrapper.setHandler(handler); return wrapper; } 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 4d93f13367f..43be8f87667 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 @@ -20,26 +20,43 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; -import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.nio.channels.ReadableByteChannel; +import java.nio.file.Path; import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EventListener; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.ListIterator; import java.util.Set; +import java.util.Spliterator; +import java.util.UUID; +import java.util.function.Consumer; -import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpServletResponseWrapper; +import org.eclipse.jetty.ee10.servlet.ErrorHandler; +import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler; +import org.eclipse.jetty.ee10.servlet.ListenerHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletMapping; +import org.eclipse.jetty.ee10.servlet.SessionHandler; +import org.eclipse.jetty.ee10.servlet.Source; +import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.WebInfConfiguration; import org.eclipse.jetty.http.HttpCookie; +import org.eclipse.jetty.http.HttpField; +import org.eclipse.jetty.http.HttpFields.Mutable; import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.http.MimeTypes.Wrapper; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; import org.eclipse.jetty.server.AbstractConnector; import org.eclipse.jetty.server.ConnectionFactory; @@ -48,28 +65,21 @@ import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.HttpCookieUtils; +import org.eclipse.jetty.server.HttpStream; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.handler.ErrorHandler; -import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.StatisticsHandler; -import org.eclipse.jetty.server.session.DefaultSessionCache; -import org.eclipse.jetty.server.session.FileSessionDataStore; -import org.eclipse.jetty.server.session.SessionHandler; -import org.eclipse.jetty.servlet.ErrorPageErrorHandler; -import org.eclipse.jetty.servlet.ListenerHolder; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.servlet.ServletMapping; -import org.eclipse.jetty.servlet.Source; -import org.eclipse.jetty.util.resource.JarResource; +import org.eclipse.jetty.session.DefaultSessionCache; +import org.eclipse.jetty.session.FileSessionDataStore; +import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.resource.CombinedResource; import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.util.resource.ResourceCollection; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.URLResourceFactory; import org.eclipse.jetty.util.thread.ThreadPool; -import org.eclipse.jetty.webapp.AbstractConfiguration; -import org.eclipse.jetty.webapp.Configuration; -import org.eclipse.jetty.webapp.WebAppContext; import org.springframework.boot.web.server.Cookie.SameSite; import org.springframework.boot.web.server.ErrorPage; @@ -161,9 +171,11 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor @Override public WebServer getWebServer(ServletContextInitializer... initializers) { JettyEmbeddedWebAppContext context = new JettyEmbeddedWebAppContext(); + context.getContext().getServletContext().setExtendedListenerTypes(true); int port = Math.max(getPort(), 0); InetSocketAddress address = new InetSocketAddress(getAddress(), port); Server server = createServer(address); + context.setServer(server); configureWebAppContext(context, initializers); server.setHandler(addHandlerWrappers(context)); this.logger.info("Server initialized with port: " + port); @@ -191,12 +203,17 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor Server server = new Server(getThreadPool()); server.setConnectors(new Connector[] { createConnector(address, server) }); server.setStopTimeout(0); + MimeTypes.Mutable mimeTypes = server.getMimeTypes(); + for (MimeMappings.Mapping mapping : getMimeMappings()) { + mimeTypes.addMimeMapping(mapping.getExtension(), mapping.getMimeType()); + } return server; } private AbstractConnector createConnector(InetSocketAddress address, Server server) { HttpConfiguration httpConfiguration = new HttpConfiguration(); httpConfiguration.setSendServerVersion(false); + httpConfiguration.setIdleTimeout(30000); List connectionFactories = new ArrayList<>(); connectionFactories.add(new HttpConnectionFactory(httpConfiguration)); if (getHttp2() != null && getHttp2().isEnabled()) { @@ -222,7 +239,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor return handler; } - private Handler applyWrapper(Handler handler, HandlerWrapper wrapper) { + private Handler applyWrapper(Handler handler, Handler.Wrapper wrapper) { wrapper.setHandler(handler); return wrapper; } @@ -239,7 +256,6 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor protected final void configureWebAppContext(WebAppContext context, ServletContextInitializer... initializers) { Assert.notNull(context, "Context must not be null"); context.clearAliasChecks(); - context.setTempDirectory(getTempDirectory()); if (this.resourceLoader != null) { context.setClassLoader(this.resourceLoader.getClassLoader()); } @@ -260,6 +276,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor context.setConfigurations(configurations); context.setThrowUnavailableOnStartupException(true); configureSession(context); + context.setTempDirectory(getTempDirectory(context)); postProcessWebAppContext(context); } @@ -289,40 +306,49 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor .forEach((locale, charset) -> context.addLocaleEncoding(locale.toString(), charset.toString())); } - private File getTempDirectory() { + private File getTempDirectory(WebAppContext context) { String temp = System.getProperty("java.io.tmpdir"); - return (temp != null) ? new File(temp) : null; + return (temp != null) + ? new File(temp, WebInfConfiguration.getCanonicalNameForWebAppTmpDir(context) + UUID.randomUUID()) + : null; } private void configureDocumentRoot(WebAppContext handler) { File root = getValidDocumentRoot(); File docBase = (root != null) ? root : createTempDir("jetty-docbase"); try { + ResourceFactory resourceFactory = handler.getResourceFactory(); List resources = new ArrayList<>(); - Resource rootResource = (docBase.isDirectory() ? Resource.newResource(docBase.getCanonicalFile()) - : JarResource.newJarResource(Resource.newResource(docBase))); - resources.add((root != null) ? new LoaderHidingResource(rootResource) : rootResource); + Resource rootResource = (docBase.isDirectory() + ? resourceFactory.newResource(docBase.getCanonicalFile().toURI()) + : resourceFactory.newJarFileResource(docBase.toURI())); + resources.add((root != null) ? new LoaderHidingResource(rootResource, rootResource) : rootResource); + URLResourceFactory urlResourceFactory = new URLResourceFactory(); for (URL resourceJarUrl : getUrlsOfJarsWithMetaInfResources()) { - Resource resource = createResource(resourceJarUrl); - if (resource.exists() && resource.isDirectory()) { + Resource resource = createResource(resourceJarUrl, resourceFactory, urlResourceFactory); + if (resource != null) { resources.add(resource); } } - handler.setBaseResource(new ResourceCollection(resources.toArray(new Resource[0]))); + handler.setBaseResource(ResourceFactory.combine(resources)); } catch (Exception ex) { throw new IllegalStateException(ex); } } - private Resource createResource(URL url) throws Exception { + private Resource createResource(URL url, ResourceFactory resourceFactory, URLResourceFactory urlResourceFactory) + throws Exception { if ("file".equals(url.getProtocol())) { File file = new File(url.toURI()); if (file.isFile()) { - return Resource.newResource("jar:" + url + "!/META-INF/resources"); + return resourceFactory.newResource("jar:" + url + "!/META-INF/resources/"); + } + if (file.isDirectory()) { + return resourceFactory.newResource(url).resolve("META-INF/resources/"); } } - return Resource.newResource(url + "META-INF/resources"); + return urlResourceFactory.newResource(url + "META-INF/resources/"); } /** @@ -333,7 +359,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor Assert.notNull(context, "Context must not be null"); ServletHolder holder = new ServletHolder(); holder.setName("default"); - holder.setClassName("org.eclipse.jetty.servlet.DefaultServlet"); + holder.setClassName("org.eclipse.jetty.ee10.servlet.DefaultServlet"); holder.setInitParameter("dirAllowed", "false"); holder.setInitOrder(1); context.getServletHandler().addServletWithMapping(holder, "/"); @@ -382,7 +408,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor * @return a configuration object for adding error pages */ private Configuration getErrorPageConfiguration() { - return new AbstractConfiguration() { + return new AbstractConfiguration(new AbstractConfiguration.Builder()) { @Override public void configure(WebAppContext context) throws Exception { @@ -399,11 +425,12 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor * @return a configuration object for adding mime type mappings */ private Configuration getMimeTypeConfiguration() { - return new AbstractConfiguration() { + return new AbstractConfiguration(new AbstractConfiguration.Builder()) { @Override public void configure(WebAppContext context) throws Exception { - MimeTypes mimeTypes = context.getMimeTypes(); + MimeTypes.Wrapper mimeTypes = (Wrapper) context.getMimeTypes(); + mimeTypes.setWrapped(new MimeTypes(null)); for (MimeMappings.Mapping mapping : getMimeMappings()) { mimeTypes.addMimeMapping(mapping.getExtension(), mapping.getMimeType()); } @@ -558,28 +585,48 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor private static final class LoaderHidingResource extends Resource { + private static final String LOADER_RESOURCE_PATH_PREFIX = "/org/springframework/boot/"; + + private final Resource base; + private final Resource delegate; - private LoaderHidingResource(Resource delegate) { + private LoaderHidingResource(Resource base, Resource delegate) { + this.base = base; this.delegate = delegate; } @Override - public Resource addPath(String path) throws IOException { - if (path.startsWith("/org/springframework/boot")) { - return null; + public void forEach(Consumer action) { + this.delegate.forEach(action); + } + + @Override + public Path getPath() { + return this.delegate.getPath(); + } + + @Override + public boolean isContainedIn(Resource r) { + return this.delegate.isContainedIn(r); + } + + @Override + public Iterator iterator() { + if (this.delegate instanceof CombinedResource) { + return list().iterator(); } - return this.delegate.addPath(path); + return List.of(this).iterator(); } @Override - public boolean isContainedIn(Resource resource) throws MalformedURLException { - return this.delegate.isContainedIn(resource); + public boolean equals(Object obj) { + return this.delegate.equals(obj); } @Override - public void close() { - this.delegate.close(); + public int hashCode() { + return this.delegate.hashCode(); } @Override @@ -587,13 +634,23 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor return this.delegate.exists(); } + @Override + public Spliterator spliterator() { + return this.delegate.spliterator(); + } + @Override public boolean isDirectory() { return this.delegate.isDirectory(); } @Override - public long lastModified() { + public boolean isReadable() { + return this.delegate.isReadable(); + } + + @Override + public Instant lastModified() { return this.delegate.lastModified(); } @@ -607,39 +664,68 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor return this.delegate.getURI(); } - @Override - public File getFile() throws IOException { - return this.delegate.getFile(); - } - @Override public String getName() { return this.delegate.getName(); } @Override - public InputStream getInputStream() throws IOException { - return this.delegate.getInputStream(); + public String getFileName() { + return this.delegate.getFileName(); } @Override - public ReadableByteChannel getReadableByteChannel() throws IOException { - return this.delegate.getReadableByteChannel(); + public InputStream newInputStream() throws IOException { + return this.delegate.newInputStream(); } @Override - public boolean delete() throws SecurityException { - return this.delegate.delete(); + public ReadableByteChannel newReadableByteChannel() throws IOException { + return this.delegate.newReadableByteChannel(); } @Override - public boolean renameTo(Resource dest) throws SecurityException { - return this.delegate.renameTo(dest); + public List list() { + return this.delegate.list().stream().filter(this::nonLoaderResource).toList(); + } + + private boolean nonLoaderResource(Resource resource) { + Path prefix = this.base.getPath().resolve(Path.of("org", "springframework", "boot")); + return !resource.getPath().startsWith(prefix); } @Override - public String[] list() { - return this.delegate.list(); + public Resource resolve(String subUriPath) { + if (subUriPath.startsWith(LOADER_RESOURCE_PATH_PREFIX)) { + return null; + } + Resource resolved = this.delegate.resolve(subUriPath); + return (resolved != null) ? new LoaderHidingResource(this.base, resolved) : null; + } + + @Override + public boolean isAlias() { + return this.delegate.isAlias(); + } + + @Override + public URI getRealURI() { + return this.delegate.getRealURI(); + } + + @Override + public void copyTo(Path destination) throws IOException { + this.delegate.copyTo(destination); + } + + @Override + public Collection getAllResources() { + return this.delegate.getAllResources().stream().filter(this::nonLoaderResource).toList(); + } + + @Override + public String toString() { + return this.delegate.toString(); } } @@ -652,6 +738,7 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor private final Set classNames; WebListenersConfiguration(Set webListenerClassNames) { + super(new AbstractConfiguration.Builder()); this.classNames = webListenerClassNames; } @@ -681,10 +768,10 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor } /** - * {@link HandlerWrapper} to apply {@link CookieSameSiteSupplier supplied} + * {@link Handler.Wrapper} to apply {@link CookieSameSiteSupplier supplied} * {@link SameSite} cookie values. */ - private static class SuppliedSameSiteCookieHandlerWrapper extends HandlerWrapper { + private static class SuppliedSameSiteCookieHandlerWrapper extends Handler.Wrapper { private final List suppliers; @@ -693,41 +780,53 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor } @Override - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) - throws IOException, ServletException { - HttpServletResponse wrappedResponse = new ResponseWrapper(response); - super.handle(target, baseRequest, request, wrappedResponse); + public boolean handle(Request request, Response response, Callback callback) throws Exception { + request.addHttpStreamWrapper((stream) -> new SameSiteCookieHttpStreamWrapper(stream, request)); + return super.handle(request, response, callback); } - class ResponseWrapper extends HttpServletResponseWrapper { + private final class SameSiteCookieHttpStreamWrapper extends HttpStream.Wrapper { - ResponseWrapper(HttpServletResponse response) { - super(response); + private final Request request; + + private SameSiteCookieHttpStreamWrapper(HttpStream wrapped, Request request) { + super(wrapped); + this.request = request; } @Override - @SuppressWarnings("removal") - public void addCookie(Cookie cookie) { - SameSite sameSite = getSameSite(cookie); - if (sameSite != null) { - String comment = HttpCookie.getCommentWithoutAttributes(cookie.getComment()); - String sameSiteComment = getSameSiteComment(sameSite); - cookie.setComment((comment != null) ? comment + sameSiteComment : sameSiteComment); + public void prepareResponse(Mutable headers) { + super.prepareResponse(headers); + ListIterator headerFields = headers.listIterator(); + while (headerFields.hasNext()) { + HttpCookieUtils.SetCookieHttpField updatedField = applySameSiteIfNecessary(headerFields.next()); + if (updatedField != null) { + headerFields.set(updatedField); + } } - super.addCookie(cookie); } - private String getSameSiteComment(SameSite sameSite) { - return switch (sameSite) { - case NONE -> HttpCookie.SAME_SITE_NONE_COMMENT; - case LAX -> HttpCookie.SAME_SITE_LAX_COMMENT; - case STRICT -> HttpCookie.SAME_SITE_STRICT_COMMENT; - }; + private HttpCookieUtils.SetCookieHttpField applySameSiteIfNecessary(HttpField headerField) { + HttpCookie cookie = HttpCookieUtils.getSetCookie(headerField); + if (cookie == null) { + return null; + } + SameSite sameSite = getSameSite(cookie); + if (sameSite == null) { + return null; + } + return new HttpCookieUtils.SetCookieHttpField( + HttpCookie.build(cookie) + .sameSite(org.eclipse.jetty.http.HttpCookie.SameSite.from(sameSite.name())) + .build(), + this.request.getConnectionMetaData().getHttpConfiguration().getResponseCookieCompliance()); } - private SameSite getSameSite(Cookie cookie) { + private SameSite getSameSite(HttpCookie cookie) { + Cookie servletCookie = new Cookie(cookie.getName(), cookie.getValue()); + cookie.getAttributes().forEach(servletCookie::setAttribute); for (CookieSameSiteSupplier supplier : SuppliedSameSiteCookieHandlerWrapper.this.suppliers) { - SameSite sameSite = supplier.getSameSite(cookie); + SameSite sameSite = supplier.getSameSite(servletCookie); if (sameSite != null) { return sameSite; } 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 bbfa74b2f6b..bbcde54a773 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 @@ -17,7 +17,6 @@ package org.springframework.boot.web.embedded.jetty; import java.io.IOException; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -29,8 +28,6 @@ import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; 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.springframework.boot.web.server.GracefulShutdownCallback; @@ -106,7 +103,7 @@ public class JettyWebServer implements WebServer { if (handler instanceof StatisticsHandler statisticsHandler) { return statisticsHandler; } - if (handler instanceof HandlerWrapper handlerWrapper) { + if (handler instanceof Handler.Wrapper handlerWrapper) { return findStatisticsHandler(handlerWrapper.getHandler()); } return null; @@ -208,7 +205,8 @@ public class JettyWebServer implements WebServer { } private String getContextPath() { - return Arrays.stream(this.server.getHandlers()) + return this.server.getHandlers() + .stream() .map(this::findContextHandler) .filter(Objects::nonNull) .map(ContextHandler::getContextPath) @@ -216,7 +214,7 @@ public class JettyWebServer implements WebServer { } private ContextHandler findContextHandler(Handler handler) { - while (handler instanceof HandlerWrapper handlerWrapper) { + while (handler instanceof Handler.Wrapper handlerWrapper) { if (handler instanceof ContextHandler contextHandler) { return contextHandler; } @@ -225,17 +223,21 @@ public class JettyWebServer implements WebServer { return null; } - private void handleDeferredInitialize(Handler... handlers) throws Exception { + private void handleDeferredInitialize(List handlers) throws Exception { for (Handler handler : handlers) { - if (handler instanceof JettyEmbeddedWebAppContext jettyEmbeddedWebAppContext) { - jettyEmbeddedWebAppContext.deferredInitialize(); - } - else if (handler instanceof HandlerWrapper handlerWrapper) { - handleDeferredInitialize(handlerWrapper.getHandler()); - } - else if (handler instanceof HandlerCollection handlerCollection) { - handleDeferredInitialize(handlerCollection.getHandlers()); - } + handleDeferredInitialize(handler); + } + } + + private void handleDeferredInitialize(Handler handler) throws Exception { + if (handler instanceof JettyEmbeddedWebAppContext jettyEmbeddedWebAppContext) { + jettyEmbeddedWebAppContext.deferredInitialize(); + } + else if (handler instanceof Handler.Wrapper handlerWrapper) { + handleDeferredInitialize(handlerWrapper.getHandler()); + } + else if (handler instanceof Handler.Collection handlerCollection) { + handleDeferredInitialize(handlerCollection.getHandlers()); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ServletContextInitializerConfiguration.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ServletContextInitializerConfiguration.java index 2b3ecbafa56..98a86fb5ba4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ServletContextInitializerConfiguration.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ServletContextInitializerConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -17,9 +17,9 @@ package org.springframework.boot.web.embedded.jetty; import jakarta.servlet.ServletException; -import org.eclipse.jetty.webapp.AbstractConfiguration; -import org.eclipse.jetty.webapp.Configuration; -import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.util.Assert; @@ -41,6 +41,7 @@ public class ServletContextInitializerConfiguration extends AbstractConfiguratio * @since 1.2.1 */ public ServletContextInitializerConfiguration(ServletContextInitializer... initializers) { + super(new AbstractConfiguration.Builder()); Assert.notNull(initializers, "Initializers must not be null"); this.initializers = initializers; } @@ -59,22 +60,13 @@ public class ServletContextInitializerConfiguration extends AbstractConfiguratio private void callInitializers(WebAppContext context) throws ServletException { try { - setExtendedListenerTypes(context, true); + context.getContext().setExtendedListenerTypes(true); for (ServletContextInitializer initializer : this.initializers) { initializer.onStartup(context.getServletContext()); } } finally { - setExtendedListenerTypes(context, false); - } - } - - private void setExtendedListenerTypes(WebAppContext context, boolean extended) { - try { - context.getServletContext().setExtendedListenerTypes(extended); - } - catch (NoSuchMethodError ex) { - // Not available on Jetty 8 + context.getContext().setExtendedListenerTypes(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 cffefa6f098..329b6a73f2f 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 @@ -111,19 +111,7 @@ class SslServerCustomizer implements JettyServerCustomizer { private SslConnectionFactory createSslConnectionFactory(SslContextFactory.Server sslContextFactory, String protocol) { - try { - return new SslConnectionFactory(sslContextFactory, protocol); - } - catch (NoSuchMethodError ex) { - // Jetty 10 - try { - return SslConnectionFactory.class.getConstructor(SslContextFactory.Server.class, String.class) - .newInstance(sslContextFactory, protocol); - } - catch (Exception ex2) { - throw new RuntimeException(ex2); - } - } + return new SslConnectionFactory(sslContextFactory, protocol); } private boolean isJettyAlpnPresent() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactoryTests.java index f8baaa8db5b..336ed38ab01 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyReactiveWebServerFactoryTests.java @@ -30,11 +30,9 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.InOrder; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests; import org.springframework.boot.web.server.Shutdown; -import org.springframework.http.client.reactive.JettyResourceFactory; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.web.reactive.function.client.WebClient; @@ -51,7 +49,6 @@ import static org.mockito.Mockito.mock; * @author Madhura Bhave * @author Moritz Halbritter */ -@Servlet5ClassPathOverrides class JettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactoryTests { @Override @@ -61,7 +58,8 @@ class JettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor @Test @Override - @Disabled("Jetty 11 does not support User-Agent-based compression") + @Disabled("Jetty 12 does not support User-Agent-based compression") + // TODO Is this true with Jetty 12? protected void noCompressionForUserAgent() { } @@ -114,20 +112,6 @@ class JettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor assertForwardHeaderIsUsed(factory); } - @Test - void useServerResources() throws Exception { - JettyResourceFactory resourceFactory = new JettyResourceFactory(); - resourceFactory.afterPropertiesSet(); - JettyReactiveWebServerFactory factory = getFactory(); - factory.setResourceFactory(resourceFactory); - JettyWebServer webServer = (JettyWebServer) factory.getWebServer(new EchoHandler()); - webServer.start(); - Connector connector = webServer.getServer().getConnectors()[0]; - assertThat(connector.getByteBufferPool()).isEqualTo(resourceFactory.getByteBufferPool()); - assertThat(connector.getExecutor()).isEqualTo(resourceFactory.getExecutor()); - assertThat(connector.getScheduler()).isEqualTo(resourceFactory.getScheduler()); - } - @Test void whenServerIsShuttingDownGracefullyThenNewConnectionsCannotBeMade() { JettyReactiveWebServerFactory factory = getFactory(); 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 560269579c6..dbb73701e06 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 @@ -40,29 +40,26 @@ import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpResponse; import org.apache.jasper.servlet.JspServlet; import org.awaitility.Awaitility; +import org.eclipse.jetty.ee10.servlet.ErrorPageErrorHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; +import org.eclipse.jetty.ee10.webapp.AbstractConfiguration; +import org.eclipse.jetty.ee10.webapp.ClassMatcher; +import org.eclipse.jetty.ee10.webapp.Configuration; +import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.server.ConnectionLimit; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; -import org.eclipse.jetty.server.handler.HandlerCollection; -import org.eclipse.jetty.server.handler.HandlerWrapper; -import org.eclipse.jetty.servlet.ErrorPageErrorHandler; -import org.eclipse.jetty.servlet.ServletHolder; 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; -import org.eclipse.jetty.webapp.ClassMatcher; -import org.eclipse.jetty.webapp.Configuration; -import org.eclipse.jetty.webapp.WebAppContext; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.InOrder; import org.springframework.boot.testsupport.system.CapturedOutput; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.server.Compression; import org.springframework.boot.web.server.GracefulShutdownResult; import org.springframework.boot.web.server.PortInUseException; @@ -89,12 +86,20 @@ import static org.mockito.Mockito.mock; * @author Henri Kerola * @author Moritz Halbritter */ -@Servlet5ClassPathOverrides class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryTests { @Override protected JettyServletWebServerFactory getFactory() { - return new JettyServletWebServerFactory(0); + JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0); + factory.addServerCustomizers((server) -> { + for (Connector connector : server.getConnectors()) { + if (connector instanceof ServerConnector serverConnector) { + // TODO Set the shutdown idle timeout in main code? + serverConnector.setShutdownIdleTimeout(10000); + } + } + }); + return factory; } @Override @@ -144,10 +149,17 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT @Test @Override - @Disabled("Jetty 11 does not support User-Agent-based compression") + @Disabled("Jetty 12 does not support User-Agent-based compression") protected void noCompressionForUserAgent() { } + @Test + @Override + @Disabled("Jetty 12 does not support SSL session tracking") + protected void sslSessionTracking() { + + } + @Test void contextPathIsLoggedOnStartupWhenCompressionIsEnabled(CapturedOutput output) { AbstractServletWebServerFactory factory = getFactory(); @@ -385,11 +397,9 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT JettyServletWebServerFactory factory = getFactory(); factory.setServerCustomizers(Collections.singletonList((server) -> { Handler handler = server.getHandler(); - HandlerWrapper wrapper = new HandlerWrapper(); + Handler.Wrapper wrapper = new Handler.Wrapper(); wrapper.setHandler(handler); - HandlerCollection collection = new HandlerCollection(); - collection.addHandler(wrapper); - server.setHandler(collection); + server.setHandler(wrapper); })); this.webServer = factory.getWebServer(exampleServletRegistration()); this.webServer.start(); @@ -507,7 +517,7 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT @Test void errorHandlerCanBeOverridden() { JettyServletWebServerFactory factory = getFactory(); - factory.addConfigurations(new AbstractConfiguration() { + factory.addConfigurations(new AbstractConfiguration(new AbstractConfiguration.Builder()) { @Override public void configure(WebAppContext context) throws Exception { @@ -544,7 +554,7 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT if (handler instanceof WebAppContext webAppContext) { return webAppContext; } - if (handler instanceof HandlerWrapper wrapper) { + if (handler instanceof Handler.Wrapper wrapper) { return findWebAppContext(wrapper.getHandler()); } throw new IllegalStateException("No WebAppContext found"); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java index 509483854ee..af2e45417fb 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/server/AbstractReactiveWebServerFactoryTests.java @@ -41,10 +41,10 @@ import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.awaitility.Awaitility; -import org.eclipse.jetty.client.api.ContentResponse; -import org.eclipse.jetty.client.util.StringRequestContent; +import org.eclipse.jetty.client.ContentResponse; +import org.eclipse.jetty.client.StringRequestContent; import org.eclipse.jetty.http2.client.HTTP2Client; -import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java index bbc5e0a06fd..2281650ef41 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/ServletComponentScanIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors. + * Copyright 2012-2023 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. @@ -38,7 +38,6 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.testsupport.classpath.ForkedClassPath; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; @@ -159,7 +158,6 @@ class ServletComponentScanIntegrationTests { } @Configuration(proxyBeanMethods = false) - @Servlet5ClassPathOverrides static class JettyTestConfiguration extends AbstractTestConfiguration { @Override diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java index 5680880dea1..24d04bdb3be 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/context/ServletWebServerMvcIntegrationTests.java @@ -22,7 +22,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories; -import org.springframework.boot.testsupport.web.servlet.Servlet5ClassPathOverrides; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; @@ -75,7 +74,6 @@ class ServletWebServerMvcIntegrationTests { } @Test - @Servlet5ClassPathOverrides void jetty() throws Exception { this.context = new AnnotationConfigServletWebServerApplicationContext(JettyConfig.class); doTest(this.context, "/hello"); @@ -88,7 +86,6 @@ class ServletWebServerMvcIntegrationTests { } @Test - @Servlet5ClassPathOverrides void advancedConfig() throws Exception { this.context = new AnnotationConfigServletWebServerApplicationContext(AdvancedConfig.class); doTest(this.context, "/example/spring/hello"); 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 0953d225aef..11f211c7369 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 @@ -99,9 +99,9 @@ import org.apache.jasper.EmbeddedServletOptions; import org.apache.jasper.servlet.JspServlet; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.awaitility.Awaitility; -import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.ContentResponse; import org.eclipse.jetty.http2.client.HTTP2Client; -import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.http2.client.transport.HttpClientTransportOverHTTP2; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; @@ -947,6 +947,7 @@ public abstract class AbstractServletWebServerFactoryTests { this.webServer = factory.getWebServer(); this.webServer.start(); ClientHttpResponse clientResponse = getClientResponse(getLocalUrl("/")); + assertThat(clientResponse.getStatusCode()).isEqualTo(HttpStatus.OK); List setCookieHeaders = clientResponse.getHeaders().get("Set-Cookie"); assertThat(setCookieHeaders).satisfiesExactlyInAnyOrder( (header) -> assertThat(header).contains("JSESSIONID").doesNotContain("SameSite"), @@ -957,7 +958,7 @@ public abstract class AbstractServletWebServerFactoryTests { } @Test - void sslSessionTracking() { + protected void sslSessionTracking() { AbstractServletWebServerFactory factory = getFactory(); Ssl ssl = new Ssl(); ssl.setEnabled(true); @@ -1017,14 +1018,12 @@ public abstract class AbstractServletWebServerFactoryTests { void mimeMappingsAreCorrectlyConfigured() { AbstractServletWebServerFactory factory = getFactory(); this.webServer = factory.getWebServer(); - Map configuredMimeMappings = getActualMimeMappings(); + Collection configuredMimeMappings = getActualMimeMappings().entrySet() + .stream() + .map((entry) -> new MimeMappings.Mapping(entry.getKey(), entry.getValue())) + .toList(); Collection expectedMimeMappings = MimeMappings.DEFAULT.getAll(); - configuredMimeMappings - .forEach((key, value) -> assertThat(expectedMimeMappings).contains(new MimeMappings.Mapping(key, value))); - for (MimeMappings.Mapping mapping : expectedMimeMappings) { - assertThat(configuredMimeMappings).containsEntry(mapping.getExtension(), mapping.getMimeType()); - } - assertThat(configuredMimeMappings).hasSameSizeAs(expectedMimeMappings); + assertThat(configuredMimeMappings).containsExactlyInAnyOrderElementsOf(expectedMimeMappings); } @Test diff --git a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle index b72b482864e..b8ff1ca48d1 100644 --- a/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle +++ b/spring-boot-tests/spring-boot-integration-tests/spring-boot-server-tests/spring-boot-server-tests-app/build.gradle @@ -41,14 +41,6 @@ configurations { } } -dependencyManagement { - jetty { - dependencies { - dependency "jakarta.servlet:jakarta.servlet-api:5.0.0" - } - } -} - tasks.register("resourcesJar", Jar) { jar -> def nested = project.resources.text.fromString("nested") from(nested) { @@ -66,7 +58,7 @@ tasks.register("resourcesJar", Jar) { jar -> } dependencies { - compileOnly("org.eclipse.jetty:jetty-server") + compileOnly("org.eclipse.jetty.ee10:jetty-ee10-servlet") compileOnly("org.springframework:spring-web") implementation("org.springframework.boot:spring-boot-starter") diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/build.gradle index 140f0a3c9a0..d740445850e 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/build.gradle @@ -11,10 +11,6 @@ configurations { } } -configurations.all { - resolutionStrategy.force("jakarta.servlet:jakarta.servlet-api:5.0.0") -} - dependencies { compileOnly(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jetty")) @@ -22,10 +18,7 @@ dependencies { exclude module: "spring-boot-starter-tomcat" } - providedRuntime("org.eclipse.jetty:apache-jsp") { - exclude group: "org.eclipse.jetty.toolchain", module: "jetty-jakarta-servlet-api" - exclude group: "org.eclipse.jetty.toolchain", module: "jetty-schemas" - } + providedRuntime("org.eclipse.jetty.ee10:jetty-ee10-apache-jsp") runtimeOnly("org.glassfish.web:jakarta.servlet.jsp.jstl") diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/src/main/resources/application.properties b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/src/main/resources/application.properties index f18efd16642..b3b89e953ed 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/src/main/resources/application.properties +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-jsp/src/main/resources/application.properties @@ -1,3 +1,4 @@ +application.message: Hello Spring Boot +server.servlet.jsp.class-name=org.eclipse.jetty.ee10.jsp.JettyJspServlet spring.mvc.view.prefix: /WEB-INF/jsp/ spring.mvc.view.suffix: .jsp -application.message: Hello Spring Boot diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-ssl/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-ssl/build.gradle index 8333e211893..72c93ab6c31 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-ssl/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty-ssl/build.gradle @@ -5,10 +5,6 @@ plugins { description = "Spring Boot Jetty SSL smoke test" -configurations.all { - resolutionStrategy.force("jakarta.servlet:jakarta.servlet-api:5.0.0") -} - dependencies { implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jetty")) implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web")) { diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/build.gradle index 94173aa1ba1..b204e4a35e3 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/build.gradle @@ -5,10 +5,6 @@ plugins { description = "Spring Boot Jetty smoke test" -configurations.all { - resolutionStrategy.force("jakarta.servlet:jakarta.servlet-api:5.0.0") -} - dependencies { implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web")) { exclude module: "spring-boot-starter-tomcat" diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties index 87709867685..0bb34b33031 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/main/resources/application.properties @@ -2,4 +2,4 @@ server.compression.enabled: true server.compression.min-response-size: 1 server.max-http-request-header-size=1000 server.jetty.threads.acceptors=2 -server.jetty.max-http-response-header-size=1000 +server.jetty.max-http-response-header-size=4096 diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/test/java/smoketest/jetty/SampleJettyApplicationTests.java b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/test/java/smoketest/jetty/SampleJettyApplicationTests.java index abde5d9033a..d72e2c2d7fc 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/test/java/smoketest/jetty/SampleJettyApplicationTests.java +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-jetty/src/test/java/smoketest/jetty/SampleJettyApplicationTests.java @@ -42,7 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Florian Storz * @author Michael Weidmann */ -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = "logging.level.org.eclipse:trace") @ExtendWith(OutputCaptureExtension.class) class SampleJettyApplicationTests { @@ -65,9 +65,8 @@ class SampleJettyApplicationTests { ResponseEntity entity = this.restTemplate.getForEntity("/", String.class); assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(entity.getBody()).isEqualTo("Hello World"); - // Jetty HttpClient decodes gzip reponses automatically - // Check that we received a gzip-encoded response - assertThat(entity.getHeaders().getFirst(HttpHeaders.CONTENT_ENCODING)).isEqualTo("gzip"); + // Jetty HttpClient decodes gzip reponses automatically and removes the + // Content-Encoding header. We have to assume that the response was gzipped. } @Test diff --git a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-websocket-jetty/build.gradle b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-websocket-jetty/build.gradle index e09d6e6806b..58e1f903b5c 100644 --- a/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-websocket-jetty/build.gradle +++ b/spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-websocket-jetty/build.gradle @@ -5,10 +5,6 @@ plugins { description = "Spring Boot WebSocket Jetty smoke test" -configurations.all { - resolutionStrategy.force("jakarta.servlet:jakarta.servlet-api:5.0.0") -} - dependencies { implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-jetty")) implementation(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-websocket")) {