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

Document how to downgrade dependencies to use embedded Jetty 11 #33044

Closed
wilkinsona opened this issue Nov 7, 2022 · 35 comments
Closed

Document how to downgrade dependencies to use embedded Jetty 11 #33044

wilkinsona opened this issue Nov 7, 2022 · 35 comments
Labels
type: documentation A documentation update
Milestone

Comments

@wilkinsona
Copy link
Member

wilkinsona commented Nov 7, 2022

In addition to downgrading the Servlet API to 5.0, we also need to cover the impact that this will have on testing. Framework's Servlet-related mocks require Servlet 6.0 and will fail due to the absence of ServletConnection when run against the Servlet 5.0 API. Similarly, Jetty requires Servlet 5.0 and will fail due to the absence of HttpSessionContext when run against the Servlet 6.0 API. This leaves us with two arrangements that will work:

  1. Tests use the Servlet 6.0 API and avoid starting Jetty by only using a mock web environment
  2. Tests use the Servlet 5.0 API and avoid using Framework's Servlet mocks by only using a full-blown web environment

If a mixture of web environments is required by an application's tests, it can be done but it may require some structural changes to separate the two web environments. With Gradle this could be done with different source sets or separate modules. With Maven, I believe separate modules will always be required.

@wilkinsona wilkinsona added the type: documentation A documentation update label Nov 7, 2022
@wilkinsona wilkinsona added this to the 3.0.x milestone Nov 7, 2022
@david-romero
Copy link

david-romero commented Nov 18, 2022

Hi @wilkinsona,

I'm facing this issue in a real application, not only in tests.
Do you know how to fix it?

Starters added:

  • spring-boot-starter-web
    • exclude
      • spring-boot-starter-tomcat
  • spring-boot-starter-jetty
  • spring-boot-starter-amqp
  • spring-boot-starter-data-redis
  • spring-boot-starter-actuator
  • spring-boot-starter-validation
  • spring-boot-starter-json
  • spring-boot-starter-webflux
  • spring-boot-starter-data-jpa

Version: 3.0.0-RC2

Error trace:

java.lang.ClassNotFoundException: jakarta.servlet.http.HttpSessionContext
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	... 41 common frames omitted
Wrapped by: java.lang.NoClassDefFoundError: jakarta/servlet/http/HttpSessionContext
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012)
	at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
	at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
	at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at org.eclipse.jetty.server.session.SessionHandler.<clinit>(SessionHandler.java:136)
	at org.eclipse.jetty.servlet.ServletContextHandler.newSessionHandler(ServletContextHandler.java:339)
	at org.eclipse.jetty.servlet.ServletContextHandler.getSessionHandler(ServletContextHandler.java:432)
	at org.eclipse.jetty.servlet.ServletContextHandler.relinkHandlers(ServletContextHandler.java:257)
	at org.eclipse.jetty.servlet.ServletContextHandler.<init>(ServletContextHandler.java:180)
	at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:301)
	at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:228)
	at org.springframework.boot.web.embedded.jetty.JettyEmbeddedWebAppContext.<init>(JettyEmbeddedWebAppContext.java:28)
	at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getWebServer(JettyServletWebServerFactory.java:158)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:183)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:161)
	... 21 common frames omitted
Wrapped by: org.springframework.context.ApplicationContextException: Unable to start web server
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:164)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:578)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)

@wilkinsona
Copy link
Member Author

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0.0-RC2-Release-Notes#jetty

@larsgrefer
Copy link
Contributor

Would it be possible to let spring-boot-starter-jetty have different dependencies?

Currently, both the Maven pom and the Gradle module explicitly request Servlet 6:

   <dependency>
     <groupId>jakarta.servlet</groupId>
     <artifactId>jakarta.servlet-api</artifactId>
     <version>6.0.0</version>
     <scope>compile</scope>
   </dependency>

https://repo.spring.io/artifactory/milestone/org/springframework/boot/spring-boot-starter-jetty/3.0.0-RC2/spring-boot-starter-jetty-3.0.0-RC2.pom

       {
         "group": "jakarta.servlet",
         "module": "jakarta.servlet-api",
         "version": {
           "requires": "6.0.0"
         }
       },

https://repo.spring.io/artifactory/milestone/org/springframework/boot/spring-boot-starter-jetty/3.0.0-RC2/spring-boot-starter-jetty-3.0.0-RC2.module

@wilkinsona
Copy link
Member Author

It's possible, although it would require some surgery to Spring Boot's build. For the vast majority of users it would have no benefit as Spring Boot's dependency management would upgrade the dependency to 6.0.0 again anyway. When we were in a similar situation with Jetty in Spring Boot 2.x (when Jetty did not support Servlet 4.0 and Tomcat and Undertow did), we used the current arrangement and it seemed to work well.

@Tristan971
Copy link

Tristan971 commented Nov 21, 2022

For the runtime setup, this works easily enough indeed, but as you rightly point out in the first message this isn't nearly as easy to work with for the test side of things (at least right now), and splitting tests into different maven modules is a rather nuclear approach to the issue.

So, realistically, most affected users will have to just stall the upgrade till Jetty supports servlet-api 6. Which might be fine or not, depending on how long it will likely take to happen. Couldn't find a reference to the matter on their repo but I'm also not too familiar with how they manage their roadmap for the upcoming Jetty 12 (which I imagine is the one that will target servlet-api 6)

@Tristan971
Copy link

Actually, scratch that, found someone asking the same question for the same reasons here https://www.eclipse.org/lists/jetty-users/msg10348.html

Quoting:

Date: Wed, 9 Nov 2022 13:59:43 -0600
Jetty 12 already has Alpha quality releases out that you can use to start experimenting with.
We should be going to Betas (or Release candidates) in the next few weeks.
Joakim Erdfelt / joakim@xxxxxxxxxxx

So hopefully not too long of a weird inter-version timeframe all-in-all (barring the usual delays one should expect with software development, ofc)

@wilkinsona
Copy link
Member Author

Unfortunately, as I understand it, there are some fairly significant breaking changes in Jetty 12. I believe @rstoyanchev saw quite a big impact on Spring Framework's WebSocket integration, for example. As such, Jetty 12 support may take some time to fully materialise.

@larsgrefer
Copy link
Contributor

It's possible, although it would require some surgery to Spring Boot's build. For the vast majority of users it would have no benefit as Spring Boot's dependency management would upgrade the dependency to 6.0.0 again anyway. When we were in a similar situation with Jetty in Spring Boot 2.x (when Jetty did not support Servlet 4.0 and Tomcat and Undertow did), we used the current arrangement and it seemed to work well.

Something like this in spring-boot-project/spring-boot-starters/spring-boot-starter-jetty/build.gradle should do the trick (at least for Gradle users):

    api("jakarta.servlet:jakarta.servlet-api") {
        version {
            strictly "5.0.0"
            because "Jetty 11 does not support Servlet 6 yet"
        }
    }

larsgrefer added a commit to joinfaces/joinfaces that referenced this issue Nov 22, 2022
@wilkinsona
Copy link
Member Author

Unfortunately, it's not that simple. Internally, Boot's build is structured such our dependency management is enforced. This ensures that we know that we're compiling and testing against the versions in our dependency management. As I said, it would require some surgery to use a different version in spring-boot-starter-jetty. We don't believe it's worth it.

@mhalbritter
Copy link
Contributor

FTR, this is how you get a working setup with gradle:

ext['jakarta-servlet.version'] = '5.0.0'

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation("org.springframework.boot:spring-boot-starter-jetty")
	modules {
		module("org.springframework.boot:spring-boot-starter-tomcat") {
			replacedBy("org.springframework.boot:spring-boot-starter-jetty", "Use Jetty instead of Tomcat")
		}
	}
}

@lpandzic
Copy link

lpandzic commented Nov 25, 2022

Unfortunately, it's not that simple. Internally, Boot's build is structured such our dependency management is enforced. This ensures that we know that we're compiling and testing against the versions in our dependency management. As I said, it would require some surgery to use a different version in spring-boot-starter-jetty. We don't believe it's worth it.

Ok, but for end users a fix would look like:

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>jakarta.servlet</groupId>
			<artifactId>jakarta.servlet-api</artifactId>
			<version>5.0.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>3.0.0</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

?

We have a company wide BOM so my plan is to do something like this unless there's another concern not do so.

@wilkinsona
Copy link
Member Author

Yes, I believe that will work as your dependency management for jakarta.servlet-api is nearer to the root so it will take precedence over what's in the imported spring-boot-dependencies.

@Sineaggi
Copy link

Sineaggi commented Nov 28, 2022

This leaves us with two arrangements that will work:

Tests use the Servlet 6.0 API and avoid starting Jetty by only using a mock web environment
Tests use the Servlet 5.0 API and avoid using Framework's Servlet mocks by only using a full-blown web environment

The first option seems simple, that sounds like using @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) which is the default.

How would we go about using the second option which avoids using Framework's Servlet mocks?

EDIT: The second option would let you use WebEnvironment.RANDOM_PORT, DEFINED_PORT and NONE.

@afranken afranken mentioned this issue Dec 5, 2022
2 tasks
@crusi
Copy link

crusi commented Dec 5, 2022

Hi @wilkinsona,

I'm facing this issue in a real application, not only in tests. Do you know how to fix it?

Starters added:

  • spring-boot-starter-web

    • exclude

      • spring-boot-starter-tomcat
  • spring-boot-starter-jetty

  • spring-boot-starter-amqp

  • spring-boot-starter-data-redis

  • spring-boot-starter-actuator

  • spring-boot-starter-validation

  • spring-boot-starter-json

  • spring-boot-starter-webflux

  • spring-boot-starter-data-jpa

Version: 3.0.0-RC2

Error trace:

java.lang.ClassNotFoundException: jakarta.servlet.http.HttpSessionContext
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	... 41 common frames omitted
Wrapped by: java.lang.NoClassDefFoundError: jakarta/servlet/http/HttpSessionContext
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012)
	at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
	at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
	at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at org.eclipse.jetty.server.session.SessionHandler.<clinit>(SessionHandler.java:136)
	at org.eclipse.jetty.servlet.ServletContextHandler.newSessionHandler(ServletContextHandler.java:339)
	at org.eclipse.jetty.servlet.ServletContextHandler.getSessionHandler(ServletContextHandler.java:432)
	at org.eclipse.jetty.servlet.ServletContextHandler.relinkHandlers(ServletContextHandler.java:257)
	at org.eclipse.jetty.servlet.ServletContextHandler.<init>(ServletContextHandler.java:180)
	at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:301)
	at org.eclipse.jetty.webapp.WebAppContext.<init>(WebAppContext.java:228)
	at org.springframework.boot.web.embedded.jetty.JettyEmbeddedWebAppContext.<init>(JettyEmbeddedWebAppContext.java:28)
	at org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory.getWebServer(JettyServletWebServerFactory.java:158)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:183)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:161)
	... 21 common frames omitted
Wrapped by: org.springframework.context.ApplicationContextException: Unable to start web server
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:164)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:578)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291)

we are facing the same issue during startup but with 3.0.0 released version. Thought we can directly jump to Boot 3 with our new service but it looks like we have to wait...

@mhalbritter
Copy link
Contributor

There are workarounds for both Maven and Gradle:

Maven

    <properties>
        <jakarta-servlet.version>5.0.0</jakarta-servlet.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>
    </dependencies>

Gradle

ext['jakarta-servlet.version'] = '5.0.0'

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation("org.springframework.boot:spring-boot-starter-jetty")
	modules {
		module("org.springframework.boot:spring-boot-starter-tomcat") {
			replacedBy("org.springframework.boot:spring-boot-starter-jetty", "Use Jetty instead of Tomcat")
		}
	}
}

Does that work for you?

@hborchardt
Copy link

Hi, just wanted to report that for me, the gradle-based version adjustment you suggest did not work, it kept pulling version 6 in. Digging through the code a little, I also did not find how that mechanism would work, the only references I found were related to maven.

What worked for me was the following snippet, based on this link: https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#managing-dependencies.gradle-bom-support.customizing

        configurations.all {          
          resolutionStrategy.eachDependency { DependencyResolveDetails details ->
            if (details.requested.group == 'jakarta.servlet') {
              details.useVersion '5.0.0'
            }
          }
        }

@eocak
Copy link

eocak commented Dec 20, 2022

FTR, this is how you get a working setup with gradle:

ext['jakarta-servlet.version'] = '5.0.0'

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation("org.springframework.boot:spring-boot-starter-jetty")
	modules {
		module("org.springframework.boot:spring-boot-starter-tomcat") {
			replacedBy("org.springframework.boot:spring-boot-starter-jetty", "Use Jetty instead of Tomcat")
		}
	}
}

This seems to be working. Thanks.

@Tak1za
Copy link

Tak1za commented Jan 12, 2023

Found a temporary fix for the problem.
Move back to Jakarta 6.0.0 and add the following dependency:


<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>${jetty.version}</version>
</dependency>

With the latest version of jetty-server.

With this the service starts up and the unit tests with MockMvc works as expected.

@simenaj96
Copy link

I tried the maven approach above, I tried adding the <jakarta-servlet.version>5.0.0</jakarta-servlet.version> in Maven properties, yet still no luck. Also tried the other approaches. But still getting that classes are not found.

@tasosz
Copy link

tasosz commented Mar 31, 2023

I've had the same issue, and the previously suggested options were not working for everything. Either the service would start or all the tests (inc mockmvc) would succeed. But not everything. :-(

In the end, this setup has worked for all:

    ext {
        set("jakarta-servlet.version", '5.0.0')
    }

plus:

    testImplementation 
            'org.eclipse.jetty:jetty-server:11.0.14',
            'jakarta.servlet:jakarta.servlet-api:6.0.0',

@nibexo
Copy link

nibexo commented Mar 31, 2023

@tasosz @simenaj96
Did you try set only
'org.eclipse.jetty:jetty-server:11.0.14' in implementation section?

@Siedlerchr
Copy link

I also tried the maven workarounds but still no success. I also tried to ugprade to jetty 12 beta maven plugin, but that didn't help. It's still the test that fail

@Siedlerchr
Copy link

Siedlerchr commented May 4, 2023

Update:

      <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
        </dependency>

        
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>${jetty.version}</version>
</dependency>

with

<properties>
<jetty.version>11.0.15</jetty.version>
<jakarta-servlet.version>5.0.0</jakarta-servlet.version>

</properties> 

worked

@Nowheresly
Copy link
Contributor

In order to use jetty AND being able to run the unit test with spring boot 3.0.x, we use the following approach:

  • downgrade jakarta-servlet version to 5.0.0 as explained in the release notes
<properties>
   <jakarta-servlet.version>5.0.0</jakarta-servlet.version>
</properties> 

  • copy and paste in the src/test/java folder the interface jakarta.servlet.ServerConnection.java. It seems adding this interface in the test classpath is enough to fix one of the main issue to run unit tests. Source code for this interface can be found here.

Obviously, the second part is clearly a hack. I'm not sure to which extend we can trust the result of unit tests (but at least they can run)....

@mikebell90
Copy link

mikebell90 commented Jun 21, 2023

@Nowheresly yours is a partial fix and works because the mockhttprequest (iirc) explicitly mocks this interface. However if you play with cookies, you'll get another error, since the Cookie class has been heavily modified.

Our "solution" is to package a local servlet-api with the cookie and servletconnection copied back.

Of course this will break should spring tests end up deliberately or accidentally referring to servlet 6 only other stuff.

opentable/servlet@5.0.0-RELEASE...opentable:servlet:5.9.0 basically makes the idea concrete.

Another limitation besides the inherent fragility is of course you CANNOT open source any thing built with this, since of course the patched version doesn't exist in maven central.

I hasten to add this is a no good fragile solution. If Spring changes or another third party relies on Servlet 6 and uses it's methods, one will live to regret this solution

@fzyzcjy
Copy link
Contributor

fzyzcjy commented Jul 24, 2023

Hi, is there any updates? Thanks!

@wilkinsona
Copy link
Member Author

@fzyzcjy No, I'm afraid not. We work in the open so any updates will appear in this issue. Anything that may be documented is already described in this issue.

@renannprado
Copy link

Thanks @Tak1za.

In the end it looks like this for me with gradle.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-web")
    implementation("org.springframework.boot:spring-boot-starter-jetty")
    modules {
        module("org.springframework.boot:spring-boot-starter-tomcat") {
            replacedBy("org.springframework.boot:spring-boot-starter-jetty", "Use Jetty instead of Tomcat")
        }
    }
    implementation("org.eclipse.jetty:jetty-server:11.0.15")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

@du-it
Copy link

du-it commented Sep 7, 2023

Unfortunately, no of the suggestions above worked for me at all.
I tried with jetty-server 12.0.1, servlet-api 5.0.0/6.0.0, spring-boot-dependencies 3.1.3 in the dependency-management, ...I always get: org.eclipse.jetty.server.handler.HandlerWrapper

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory]: Factory method 'jettyServletWebServerFactory' threw exception with message: org/eclipse/jetty/server/handler/HandlerWrapper
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:171)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
	... 97 more
Caused by: java.lang.NoClassDefFoundError: org/eclipse/jetty/server/handler/HandlerWrapper
	at org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedJetty.jettyServletWebServerFactory(ServletWebServerFactoryConfiguration.java:93)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:139)
	... 98 more
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.server.handler.HandlerWrapper
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)

@nibexo
Copy link

nibexo commented Sep 7, 2023

@du-it
Did you try use version 11.0.14 instead 12.0.1?

@wilkinsona
Copy link
Member Author

@du-it As I already said on Stack Overflow Jetty 12 is not yet supported. #36073 will add Jetty 12 support and will hopefully be part of Spring Boot 3.2. In the meantime, as @nibexo suggests, you need to use Jetty 11.0.x.

@asciicode
Copy link

Found a temporary fix for the problem. Move back to Jakarta 6.0.0 and add the following dependency:


<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>${jetty.version}</version>
</dependency>

With the latest version of jetty-server.

With this the service starts up and the unit tests with MockMvc works as expected.

you save my day.

@mhalbritter
Copy link
Contributor

mhalbritter commented Oct 27, 2023

A part of this has been done while working on #37238.

@maddy65
Copy link

maddy65 commented Mar 4, 2024

I have a similar issue and I have used something like that

resolutionStrategy.eachDependency { details ->
	if (details.requested.group == 'jakarta.servlet') {
		// to support jetty on spring boot 3.1.4
		details.useVersion '5.0.0'
	}
}

After adding this in configuration of build.gradle my application is working fine but I am getting issue while running the testcase.

java.lang.NoClassDefFoundError: jakarta/servlet/ServletConnection
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:210)
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:241)
	at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$8(ClassBasedTestDescriptor.java:363)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:368)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$9(ClassBasedTestDescriptor.java:363)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:362)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:283)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:282)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:272)
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:271)
	at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:102)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:101)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

Any workaround how to manage both application and testcases for the same.

@Spikhalskiy
Copy link

I faced the same issue with java.lang.NoClassDefFoundError: jakarta/servlet/ServletConnection coming from the usage in org.springframework.mock.web.MockHttpServletRequest.
Environment: jetty 11 + jakarta.servlet-api 5 + Spring 6.1.
I was able to work around the problem by just adding ServletConnection to the project classpath (can be done by adding the source code of the class directly to the project).

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

No branches or pull requests