How to disable autoconfiguration for undertow

Viewed 1704

I have a normal spring-boot web application using spring-boot-starter-web, i.e. an embedded tomcat.

Now one of the libraries I'm using for testing comes with undertow as a dependency (because it itself is starting an embedded webserver for mocking an external dependency), and this seems to get the spring-boot autoconfiguration to try to configure undertow as the embedded web server (which seems to break due to version mismatches, and is also not what I want – I'm fine with tomcat as my server).

Here is our test class:

package org.zalando.nakadiproducer.tests;

[... imports skipped ...]

import static io.restassured.RestAssured.given;

@RunWith(SpringRunner.class)
@SpringBootTest(
        // This line looks like that by intention: We want to test that the MockNakadiPublishingClient will be picked up
        // by our starter *even if* it has been defined *after* the application itself. This has been a problem until
        // this commit.
        classes = { Application.class, MockNakadiConfig.class  },
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
//@EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.class)
public class ApplicationIT {
    @LocalManagementPort
    private int localManagementPort;

    @ClassRule
    public static final EnvironmentVariables environmentVariables
            = new EnvironmentVariables();

    @BeforeClass
    public static void fakeCredentialsDir() {
        environmentVariables.set("CREDENTIALS_DIR", new File("src/main/test/tokens").getAbsolutePath());
    }

    @Test
    public void shouldSuccessfullyStartAndSnapshotCanBeTriggered() {
        given().baseUri("http://localhost:" + localManagementPort).contentType("application/json")
        .when().post("/actuator/snapshot-event-creation/eventtype")
        .then().statusCode(204);
    }
}

With the main application class:

package org.zalando.nakadiproducer.tests;

[imports skipped]

@EnableAutoConfiguration
@EnableNakadiProducer
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    @Primary
    public DataSource dataSource() throws IOException {
        return embeddedPostgres().getPostgresDatabase();
    }

    @Bean
    public EmbeddedPostgres embeddedPostgres() throws IOException {
        return EmbeddedPostgres.start();
    }

    @Bean
    public SnapshotEventGenerator snapshotEventGenerator() {
        return new SimpleSnapshotEventGenerator("eventtype", (withIdGreaterThan, filter) -> {
            if (withIdGreaterThan == null) {
                return Collections.singletonList(new Snapshot("1", "foo", filter));
            } else if (withIdGreaterThan.equals("1")) {
                return Collections.singletonList(new Snapshot("2", "foo", filter));
            } else {
                return new ArrayList<>();
            }
        });

        // Todo: Test that some events arrive at a local nakadi mock
    }
}

This is the error message:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'undertowWebServerFactoryCustomizer' defined in class path resource [org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration$UndertowWebServerFactoryCustomizerConfiguration.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer] from ClassLoader [sun.misc.Launcher$AppClassLoader@378fd1ac]

The mentioned definition class is in spring-boot-autoconfigure 2.0.3.RELEASE, and looks like this:

@Configuration
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {

    @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
    public static class TomcatWebServerFactoryCustomizerConfiguration {

 // tomcat, jetty

    /**
     * Nested configuration if Undertow is being used.
     */
    @Configuration
    @ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
    public static class UndertowWebServerFactoryCustomizerConfiguration {

        @Bean
        public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(
                Environment environment, ServerProperties serverProperties) {
            return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
        }
    }
}

How can I tell spring-boot to not configure Undertow?

I tried @EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.class) on my test class (beside @SpringBootTest), but that has no effect.

If I try @EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.UndertowWebServerFactoryCustomizerConfiguration.class), I get this error:

Caused by: java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
    - org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$UndertowWebServerFactoryCustomizerConfiguration
1 Answers

Removing Undertow from your project's dependencies is the safest way. Spring Boot is based on classpath scanning so once Undertow is gone from the classpath it's auto-configuration won't be processed.

The problem with EmbeddedWebServerFactoryCustomizerAutoConfiguration is that it doesn't provide a property switch. It's purely based on servlet container class presence. To get rid of it, you would have to exclude the entire EmbeddedWebServerFactoryCustomizerAutoConfiguration:

@EnableAutoConfiguration(exclude=EmbeddedWebServerFactoryCustomizerAutoConfiguration.class)
public MyTest {

}

and in your test configuration define just the beans for starting Tomcat:

@TestConfiguraton
@EnableConfigurationProperties(ServerProperties.class)
public MyTestConfig {

  @Bean
  public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
    return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
  }

}
Related