I would like to add Gretty to my existing Spring Boot project to start / stop a Jetty server before / after my integration tests.
Unfortunately, after adding the Gretty plugin to my build.gradle.kts, the unit tests fail with the following error.
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletException
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)
... 87 more
If I look at the Gradle dependencies, it seems that the Gretty plugin pulls in a dependency on javax.servlet-api and the Spring framework pulls in a dependency on jakarta.servlet-api.
Any recommendations on how to solve this?
Here is my build.gradle.kts.
plugins {
java
id("org.gretty") version "4.0.2"
id("org.springframework.boot") version "2.6.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
group = "eu.mneifercons.examples"
version = "0.0.1-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springdoc:springdoc-openapi-ui:1.6.8")
implementation("org.glassfish.jaxb:jaxb-runtime")
}
testing {
suites {
val test by getting(JvmTestSuite::class) {
testType.set(TestSuiteType.UNIT_TEST)
useJUnitJupiter()
dependencies {
implementation("org.springframework.boot:spring-boot-starter-test")
implementation("io.rest-assured:spring-mock-mvc:4.5.0")
}
}
val integrationTest by registering(JvmTestSuite::class) {
testType.set(TestSuiteType.INTEGRATION_TEST)
dependencies {
implementation(project)
implementation("io.rest-assured:rest-assured:4.5.1")
implementation("io.rest-assured:json-path:4.5.1")
implementation("io.rest-assured:xml-path:4.5.1")
}
sources {
java {
setSrcDirs(listOf("src/it/java"))
}
}
targets {
all {
testTask.configure {
shouldRunAfter(test)
}
}
}
}
}
}
tasks.named("check") {
dependsOn(testing.suites.named("integrationTest"))
}
gretty {
integrationTestTask = "integrationTest"
}
I would start with getting rid of the Spring dependency management plugin if you only use it to consume the Spring BOM.
It imho ususally does more harm than good by doing more than that, like porting over conceptually broken behavior from Maven to Gradle. If it is only about the versions, just use the native BOM support of Gradle using a platform dependency declaration.
I don’t know whether it causes your issue or will help, but imho definitely a step you should go unless you depend on some of the additional features of that plugin.
Thank you very much for your reply and your suggestion.
I should have added that almost everything you see in my build file is copy ‘n’ paste from tutorials or Stackoverflow threads. I’m a long time Maven user and this is my first Gradle project. I have experience using Maven Surefire and Failsafe plugins and I’m trying to create a similar workflow with Gradle. Feel free to point me to the documentation as I’m having the impression that I’m still missing some important Gradle concepts.
After removing the Spring dependency management plugin, execution of gradle clean test failed with some errors “Could not resolve all files for configuration ‘:compileClasspath’.”
I changed my dependencies as follows and the compile was successful again.
However, when I run gradle clean test now, I still get the ClassNotFoundException.
Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletException
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)
... 141 more
Tutorials and SO answers are always dangerous, especially if they are not recent in the context of a rapidly developing project like Gradle.
You should not add the explicit version to the Spring boot dependencies, but use a platform dependency as I already said and as is documented at Spring Boot Gradle Plugin Reference Guide.
I also would suggest you do not use ...Compatibility settings, but the more sophisticated JVM toolchains instead: Toolchains for JVM projects
Also if you are not building a library but an end product, setting group is pretty pointless.
You can do it of course, but it is just pointless.
Another point when coming from Maven, this is Gradle, not Maven, stop the practice of always using clean. That is something essential for most Maven builds as they are unstable and flaky and you often have to always use clean to get reliable results. In Gradle this is not the case. If you need to use clean to get a reliable result, that is a bug that should be fixed instead, most often in the build script. By always using clean you are working against the core Gradle strengths which are avoiding work that is not necessary.
Regarding the actual problem, you maybe have to talk to the Gretty plugin developers if their plugin is causing this.
But you probably have to provide an MCVE, because I actually tried to quickly reproduce your problem and there was no problem.
I know about the dangers of outdated tutorials and SO answers, I just have to start somewhere. But thanks for the reminder!
I will read your suggestions about platform dependencies, the ...Compatibility settings and about setting group. Interesting comments about using clean. Yes, I developed this habit over the years with Maven. Expect new threads from me with more questions about all this. Puh, the time has come, the walrus said, to learn of many things.
Excellent idea to create an MCVE. It’s good to know that it worked for you. It seems that my ideas are not complete nonsense. Will post my findings here. However, as this is just one of my side-projects to learn new stuff, it might take a while.