Skip to content

Commit

Permalink
Support Jetty 10
Browse files Browse the repository at this point in the history
Closes gh-24886
  • Loading branch information
wilkinsona committed Feb 17, 2021
1 parent a95e93a commit df5f591
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ Spring Boot supports the following embedded servlet containers:
| Jetty 9.4
| 3.1

| Jetty 10.0
| 4.0

| Undertow 2.0
| 4.0
|===
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ The following Maven example shows how to exclude Tomcat and include Jetty for Sp
</dependency>
----

NOTE: The version of the Servlet API has been overridden as, unlike Tomcat 9 and Undertow 2.0, Jetty 9.4 does not support Servlet 4.0.
NOTE: The version of the Servlet API has been overridden as, unlike Tomcat 9 and Undertow 2, Jetty 9.4 does not support Servlet 4.0.
If you wish to use Jetty 10, which does support Servlet 4.0, override the `jetty.version` property rather than the `servlet-api.version` property.

The following Gradle example shows how to use Undertow in place of Reactor Netty for Spring WebFlux:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,7 +16,10 @@

package org.springframework.boot.web.embedded.jetty;

import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.function.Supplier;

import org.apache.commons.logging.Log;
Expand All @@ -27,6 +30,7 @@
import org.springframework.boot.web.server.GracefulShutdownCallback;
import org.springframework.boot.web.server.GracefulShutdownResult;
import org.springframework.core.log.LogMessage;
import org.springframework.util.ReflectionUtils;

/**
* Handles Jetty graceful shutdown.
Expand All @@ -50,23 +54,44 @@ final class GracefulShutdown {

void shutDownGracefully(GracefulShutdownCallback callback) {
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
boolean jetty10 = isJetty10();
for (Connector connector : this.server.getConnectors()) {
shutdown(connector);
shutdown(connector, !jetty10);
}
this.shuttingDown = true;
new Thread(() -> awaitShutdown(callback), "jetty-shutdown").start();

}

private void shutdown(Connector connector) {
@SuppressWarnings("unchecked")
private void shutdown(Connector connector, boolean getResult) {
Future<Void> result;
try {
connector.shutdown().get();
result = connector.shutdown();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
catch (NoSuchMethodError ex) {
Method shutdown = ReflectionUtils.findMethod(connector.getClass(), "shutdown");
result = (Future<Void>) ReflectionUtils.invokeMethod(shutdown, connector);
}
if (getResult) {
try {
result.get();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
catch (ExecutionException ex) {
// Continue
}
}
}

private boolean isJetty10() {
try {
return CompletableFuture.class.equals(Connector.class.getMethod("shutdown").getReturnType());
}
catch (ExecutionException ex) {
// Continue
catch (Exception ex) {
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,6 +21,7 @@
import java.util.HashSet;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

Expand Down Expand Up @@ -49,11 +50,11 @@ public boolean errorPageForMethod(String method) {

@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException {
throws IOException, ServletException {
if (!HANDLED_HTTP_METHODS.contains(baseRequest.getMethod())) {
baseRequest.setMethod("GET");
}
super.doError(target, baseRequest, request, response);
super.handle(target, baseRequest, request, response);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -47,7 +47,12 @@ static HandlerWrapper createGzipHandlerWrapper(Compression compression) {
handler.addIncludedMethods(httpMethod.name());
}
if (compression.getExcludedUserAgents() != null) {
handler.setExcludedAgentPatterns(compression.getExcludedUserAgents());
try {
handler.setExcludedAgentPatterns(compression.getExcludedUserAgents());
}
catch (NoSuchMethodError ex) {
// Jetty 10 does not support User-Agent-based exclusions
}
}
return handler;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
Expand Down Expand Up @@ -70,6 +71,7 @@
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

/**
Expand Down Expand Up @@ -306,7 +308,16 @@ protected final void addDefaultServlet(WebAppContext context) {
holder.setInitParameter("dirAllowed", "false");
holder.setInitOrder(1);
context.getServletHandler().addServletWithMapping(holder, "/");
context.getServletHandler().getServletMapping("/").setDefault(true);
ServletMapping servletMapping = context.getServletHandler().getServletMapping("/");
try {
servletMapping.setDefault(true);
}
catch (NoSuchMethodError ex) {
// Jetty 10
Method setFromDefaultDescriptor = ReflectionUtils.findMethod(servletMapping.getClass(),
"setFromDefaultDescriptor", boolean.class);
ReflectionUtils.invokeMethod(setFromDefaultDescriptor, servletMapping, true);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.util.component.AbstractLifeCycle;

import org.springframework.boot.web.server.GracefulShutdownCallback;
import org.springframework.boot.web.server.GracefulShutdownResult;
Expand Down Expand Up @@ -120,18 +119,7 @@ private void initialize() {
// Cache the connectors and then remove them to prevent requests being
// handled before the application context is ready.
this.connectors = this.server.getConnectors();
this.server.addBean(new AbstractLifeCycle() {

@Override
protected void doStart() throws Exception {
for (Connector connector : JettyWebServer.this.connectors) {
Assert.state(connector.isStopped(),
() -> "Connector " + connector + " has been started prematurely");
}
JettyWebServer.this.server.setConnectors(null);
}

});
JettyWebServer.this.server.setConnectors(null);
// Start the server so that the ServletContext is available
this.server.start();
this.server.setStopAtShutdown(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -106,8 +106,22 @@ private ServerConnector createServerConnector(Server server, SslContextFactory.S
private ServerConnector createHttp11ServerConnector(Server server, HttpConfiguration config,
SslContextFactory.Server sslContextFactory) {
HttpConnectionFactory connectionFactory = new HttpConnectionFactory(config);
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory,
HttpVersion.HTTP_1_1.asString());
SslConnectionFactory sslConnectionFactory;
try {
sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString());
}
catch (NoSuchMethodError ex) {
// Jetty 10
try {
sslConnectionFactory = SslConnectionFactory.class
.getConstructor(SslContextFactory.Server.class, String.class)
.newInstance(sslContextFactory, HttpVersion.HTTP_1_1.asString());
}
catch (Exception ex2) {
throw new RuntimeException(ex2);
}
}

return new SslValidatingServerConnector(server, sslContextFactory, this.ssl.getKeyAlias(), sslConnectionFactory,
connectionFactory);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2012-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.web.embedded.jetty;

import org.eclipse.jetty.server.handler.ErrorHandler;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;

import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import org.springframework.boot.testsupport.classpath.ClassPathOverrides;

import static org.assertj.core.api.Assertions.assertThat;

@EnabledForJreRange(min = JRE.JAVA_11)
@ClassPathExclusions({ "jetty-*.jar", "tomcat-embed*.jar" })
@ClassPathOverrides({ "org.slf4j:slf4j-api:1.7.25", "org.eclipse.jetty:jetty-io:10.0.0",
"org.eclipse.jetty:jetty-server:10.0.0", "org.eclipse.jetty:jetty-servlet:10.0.0",
"org.eclipse.jetty:jetty-util:10.0.0", "org.eclipse.jetty:jetty-webapp:10.0.0",
"org.eclipse.jetty.http2:http2-common:10.0.0", "org.eclipse.jetty.http2:http2-hpack:10.0.0",
"org.eclipse.jetty.http2:http2-server:10.0.0", "org.mortbay.jasper:apache-jsp:8.5.40" })
public class Jetty10ServletWebServerFactoryTests extends JettyServletWebServerFactoryTests {

@Override
@Test
protected void correctVersionOfJettyUsed() {
String jettyVersion = ErrorHandler.class.getPackage().getImplementationVersion();
assertThat(jettyVersion.startsWith("10.0"));
}

@Override
@Disabled("Jetty 10 does not support User-Agent-based compression")
protected void noCompressionForUserAgent() {

}

@Override
@Disabled("Jetty 10 adds methods to Configuration that we can't mock while compiling against 9")
protected void jettyConfigurations() throws Exception {
}

}
Loading

0 comments on commit df5f591

Please sign in to comment.