Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebSocket auto-configuration throws a CNFE with Jetty 10 #26847

Closed
christophe-pietquin opened this issue Jun 10, 2021 · 7 comments
Closed

WebSocket auto-configuration throws a CNFE with Jetty 10 #26847

christophe-pietquin opened this issue Jun 10, 2021 · 7 comments
Assignees
Labels
type: bug A general bug
Milestone

Comments

@christophe-pietquin
Copy link

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
@snicoll snicoll changed the title Spring boot 2.5.0 can't start with jetty 10 ClassNotFoundException: org.eclipse.jetty.websocket.servlet.WebSocketCreator WebSocket auto-configuration throws a CNFE with Jetty 10 Jun 10, 2021
@snicoll snicoll added the type: bug A general bug label Jun 10, 2021
@snicoll snicoll added this to the 2.5.x milestone Jun 10, 2021
@snicoll
Copy link
Member

snicoll commented Jun 10, 2021

Thanks Christophe, I've reproduced the problem.

@snicoll
Copy link
Member

snicoll commented Jun 10, 2021

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

@SpringBootApplication(exclude = WebSocketServletAutoConfiguration.class)

@snicoll
Copy link
Member

snicoll commented Jun 10, 2021

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

@wilkinsona
Copy link
Member

wilkinsona commented Jun 11, 2021

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.

@wilkinsona
Copy link
Member

wilkinsona commented Jun 11, 2021

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.

@leozhang123
Copy link

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

@snicoll
Copy link
Member

snicoll commented Sep 26, 2021

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

No branches or pull requests

4 participants