spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.22k stars 40.7k forks source link

Spring boot test fails to start tomcat due to some sleuth and spring-integration dependencies #9934

Closed zxkane closed 7 years ago

zxkane commented 7 years ago

It happens on a project using Spring webflux via Spring boot 2.0.0.M3. Below is the dependencies of project,

    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-actuator',
            'org.springframework.cloud:spring-cloud-starter-config',
            'org.springframework.cloud:spring-cloud-sleuth-stream',
            'org.springframework.cloud:spring-cloud-starter-sleuth',
            'org.springframework.cloud:spring-cloud-starter-stream-rabbit',
            'org.springframework.boot:spring-boot-starter-data-mongodb-reactive',
            'org.springframework.boot:spring-boot-starter-data-redis-reactive',
            'org.springframework.boot:spring-boot-starter-integration',
            "org.springframework.integration:spring-integration-amqp",
            "org.springframework.integration:spring-integration-mongodb",
            'org.springframework.retry:spring-retry',
            'org.springframework.boot:spring-boot-starter-webflux',
            'org.springframework.boot:spring-boot-starter-reactor-netty',
            'com.fasterxml.jackson.datatype:jackson-datatype-joda',
            'joda-time:joda-time:2.9.9',
            'org.javamoney:moneta:1.0',
            'com.squareup.okhttp3:okhttp:3.8.1',
            'org.apache.commons:commons-lang3:3.5'
    compileOnly 'org.projectlombok:lombok:1.16.18'
    testCompile 'org.springframework.boot:spring-boot-starter-test',
            'io.projectreactor:reactor-test',
            'org.apache.qpid:qpid-broker:6.1.2',
            'de.flapdoodle.embed:de.flapdoodle.embed.mongo'
    }

The app is running well via ./gradlew bootRun or running main app directly.

However I failed to start integration test due to below error.

Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat

I'm wondering why WebTestClient still tries to use embedded tomcat even though we are using webflux that uses reactive-netty by default.

Is it a bug of spring boot test?

Below is code snippet of my test case,

    @RunWith(SpringRunner.class)
    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class NoteHandlerTest {

    @Autowired
    private WebTestClient webClient;

    @Test
    public void testNoteNotFound() throws Exception {
        this.webClient.get().uri("/note/request/{id}", "nosuchid").accept(MediaType.APPLICATION_JSON_UTF8)
                .exchange().expectStatus().isNotFound();
    }
    }
bclozel commented 7 years ago

Your build is bringing spring-boot-starter-tomcat as a transitive dependency (probably from the spring-cloud ones). Right now Spring Boot will auto-configure Tomcat as a reactive runtime if it is present on the classpath. Using Tomcat with WebFlux is perfectly fine and is supported by Spring Boot.

Note that the reactive support in Spring Cloud is work in progress, so you shouldn't rely on such features for now.

Unfortunately, you issue description doesn't help much with the issue. A repro project (or at least a complete stacktrace) could help here.

zxkane commented 7 years ago

@bclozel Thanks for your feedback. It's totally fine for me to use tomcat with webflux.

I created a sample repo to demonstrate this issue. Looks like the dependencies sleuth, spring-integration-amqp caused spring boot test failed to start tomcat in integration test.

Below is the verbose exception stack,


java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:107)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:242)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:137)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:122)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:138)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
    ... 25 more
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:114)
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:81)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:527)
    at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:185)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:161)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:134)
    ... 33 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardServer[-1]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
    at org.apache.catalina.startup.Tomcat.start(Tomcat.java:367)
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:95)
    ... 38 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardService[Tomcat]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:793)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 40 more
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:422)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 42 more
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:947)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 44 more

In the sample repo, the master has problem to run test case com.github.zxkane.handler.NoteHandlerTest#testGetNoteNotFound due to above error. The another branch working the case runs well after shaping the suspicious dependencies.

Let me know if you need more information.

wilkinsona commented 7 years ago

The actual failure occurs earlier:

java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]
    at java.util.concurrent.FutureTask.report(FutureTask.java:122)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:939)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:872)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
    ... 6 common frames omitted
Caused by: org.apache.catalina.LifecycleException: Failed to start component [Pipeline[StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[]]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5117)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 6 common frames omitted
Caused by: org.apache.catalina.LifecycleException: Failed to start component [org.apache.catalina.authenticator.NonLoginAuthenticator[]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
    at org.apache.catalina.core.StandardPipeline.startInternal(StandardPipeline.java:182)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 8 common frames omitted
Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String;
    at org.apache.catalina.authenticator.AuthenticatorBase.startInternal(AuthenticatorBase.java:1141)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    ... 10 common frames omitted

You have the Servlet 3.0 API on the classpath via your test dependency on QPid. The version of Tomcat that you're using requires the Servlet 3.1 API so you need to exclude this unwanted dependency.