When using Jetty version 10.0.0 up to 10.0.3 and Java 11 (openjdk 11.0.7 2020-04-14) Spring Boot 2.5.0 can't start.

It can be reproduced from https://start.spring.io/

Modify the pom.xml as per Spring Boot 2.5.documentation

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
        <relativePath/>
        <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <jetty.version>10.0.3</jetty.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

It ends up in the following stacktrace :

2021-06-10 08:41:04.659 ERROR 850316 --- [ main] o.s.boot.SpringApplication : Application run failed

2021-06-10 08:53:04.698 ERROR 853100 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Jetty web server
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:163) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577) ~[spring-context-5.3.7.jar!/:5.3.7]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:337) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1336) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1325) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at com.example.demo.DemoApplication.main(DemoApplication.java:10) ~[classes!/:0.0.1-SNAPSHOT]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Jetty web server
        at org.springframework.boot.web.embedded.jetty.JettyWebServer.initialize(JettyWebServer.java:129) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.web.embedded.jetty.JettyWebServer.<init>(JettyWebServer.java:90) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getJettyWebServer(JettyServletWebServerFactory.java:429) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getWebServer(JettyServletWebServerFactory.java:170) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:182) ~[spring-boot-2.5.0.jar!/:2.5.0]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160) ~[spring-boot-2.5.0.jar!/:2.5.0]
        ... 16 common frames omitted
Caused by: java.lang.NoClassDefFoundError: org/eclipse/jetty/websocket/servlet/WebSocketCreator
        at org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer.initialize(NativeWebSocketServletContainerInitializer.java:63) ~[websocket-server-9.4.41.v20210516.jar!/:9.4.41.v20210516]
        at org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer.initialize(WebSocketServerContainerInitializer.java:196) ~[javax-websocket-server-impl-9.4.41.v20210516.jar!/:9.4.41.v20210516]
        at org.springframework.boot.autoconfigure.websocket.servlet.JettyWebSocketServletWebServerCustomizer$1.configure(JettyWebSocketServletWebServerCustomizer.java:46) ~[spring-boot-autoconfigure-2.5.0.jar!/:2.5.0]
        at org.eclipse.jetty.webapp.Configurations.configure(Configurations.java:508) ~[jetty-webapp-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.webapp.WebAppContext.configure(WebAppContext.java:514) ~[jetty-webapp-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1300) ~[jetty-webapp-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:879) ~[jetty-server-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:306) ~[jetty-servlet-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:532) ~[jetty-webapp-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) ~[jetty-util-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171) ~[jetty-util-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.server.Server.start(Server.java:469) ~[jetty-server-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114) ~[jetty-util-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:89) ~[jetty-server-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.server.Server.doStart(Server.java:414) ~[jetty-server-10.0.3.jar!/:10.0.3]
        at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93) ~[jetty-util-10.0.3.jar!/:10.0.3]
        at org.springframework.boot.web.embedded.jetty.JettyWebServer.initialize(JettyWebServer.java:123) ~[spring-boot-2.5.0.jar!/:2.5.0]
        ... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.websocket.servlet.WebSocketCreator
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471) ~[na:na]
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589) ~[na:na]
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:151) ~[demo-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[na:na]
        ... 38 common frames omitted

Comment From: snicoll

Thanks Christophe, I've reproduced the problem.

Comment From: snicoll

Workaround for the time being is to exclude the auto-configuration, e.g.:

@SpringBootApplication(exclude = WebSocketServletAutoConfiguration.class)

Comment From: snicoll

Our starter has a direct dependency on org.eclipse.jetty.websocket:websocket-server. In Jetty 10, this library does not exist and the replacement seems to be websocket-jetty-server.

A sample project above means that we still bring websocket-server on the classpath (v9) and we don't bring Jetty 10's WebSocket support. Patching the pom to bring the necessary jar still breaks with a different type this time:

Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.websocket.server.NativeWebSocketServletContainerInitializer
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) ~[na:na]
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ~[na:na]
    ... 29 common frames omitted

Comment From: wilkinsona

I've updated the release notes to reflect the current situation. Rather than excluding the WebSocket auto-configuration, I'd recommend excluding the unwanted Jetty 9-specific dependencies instead. This avoids having Jetty modules from two different versions on the classpath.

Comment From: wilkinsona

To get WebSockets going with Jetty 10 two additional dependencies are needed:

  • org.eclipse.jetty.websocket:websocket-javax-server
  • org.eclipse.jetty.websocket:websocket-jetty-server

You also need to add a WebServerFactoryCustomizer to replace the auto-configuration which has backed off due to Jetty 9's org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer being absent:

class JettyWebSocketServletWebServerCustomizer implements WebServerFactoryCustomizer<JettyServletWebServerFactory>, Ordered {

    @Override
    public void customize(JettyServletWebServerFactory factory) {
        factory.addConfigurations(new AbstractConfiguration() {

            @Override
            public void configure(WebAppContext context) throws Exception {
                Context servletContext = context.getServletContext();
                JettyWebSocketServerContainer jettyContainer = JettyWebSocketServerContainer.getContainer(servletContext);
                Server server = context.getServer();
                if (jettyContainer == null) {
                    WebSocketServerComponents.ensureWebSocketComponents(server, servletContext);
                    JettyWebSocketServerContainer.ensureContainer(servletContext);
                }
                JavaxWebSocketServerContainer javaxContainer = JavaxWebSocketServerContainer.getContainer(servletContext);
                if (javaxContainer == null) {
                    WebSocketServerComponents.ensureWebSocketComponents(server, servletContext);
                    WebSocketUpgradeFilter.ensureFilter(servletContext);
                    WebSocketMappings.ensureMappings(servletContext);
                    JavaxWebSocketServerContainer.ensureContainer(servletContext);
                }
            }

        });
    }

    @Override
    public int getOrder() {
        return 0;
    }

}

We should be able to include the customiser (modified to use reflection) in the auto-configuration so that only the dependency juggling is necessary.

Comment From: leozhang123

Has this problem been fixed? I also found this problem in the new version. springboot 2.5.5 and jdk 17,jetty 10.0.6

Comment From: snicoll

@leozhang123 I am not sure I understand your question. This issue is closed in a milestone with additional tests, so yes we believe it is fixed. If you think you've found an issue, please create a separate issue with a small sample we can run ourselves. Thank you.