From 2be9d7f91c71861e9fe5e588b1a669d024ccaf2b Mon Sep 17 00:00:00 2001 From: spencergibb Date: Tue, 20 Apr 2021 13:15:59 -0400 Subject: [PATCH] Suggest setting spring.main.web-application-type=reactive if mvc on classpath. See gh-2176K --- .../mvc-failure-analyzer/pom.xml | 4 ++ .../sample/MvcFailureAnalyzerApplication.java | 38 +++++++++++++++++++ .../MvcFailureAnalyzerApplicationTests.java | 31 +++++++++++++-- ...ewayClassPathWarningAutoConfiguration.java | 2 + .../MvcFoundOnClasspathFailureAnalyzer.java | 14 +++++-- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml index f7bfa256..d44fe799 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/pom.xml @@ -29,6 +29,10 @@ org.springframework.cloud spring-cloud-starter-gateway + + org.springframework.cloud + spring-cloud-starter-loadbalancer + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/main/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplication.java b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/main/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplication.java index f9c17d7d..8bb621ef 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/main/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplication.java +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/main/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplication.java @@ -18,12 +18,50 @@ package org.springframework.cloud.gateway.sample; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.client.DefaultServiceInstance; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; /** * @author Spencer Gibb */ @SpringBootConfiguration @EnableAutoConfiguration +@RestController +@LoadBalancerClient(name = "myservice", configuration = MyServiceConf.class) public class MvcFailureAnalyzerApplication { + @GetMapping("hello") + public String hello() { + return "Hello"; + } + + @Bean + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) + public RouteLocator myRouteLocator(RouteLocatorBuilder builder) { + return builder.routes().route(r -> r.path("/myprefix/**") + .filters(f -> f.stripPrefix(1)).uri("lb://myservice")).build(); + } + +} + +class MyServiceConf { + + @LocalServerPort + private int port = 0; + + @Bean + public ServiceInstanceListSupplier staticServiceInstanceListSupplier() { + return ServiceInstanceListSuppliers.from("myservice", new DefaultServiceInstance( + "myservice-1", "myservice", "localhost", port, false)); + } + } diff --git a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/test/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplicationTests.java b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/test/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplicationTests.java index a73fb679..ed8c9b82 100644 --- a/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/test/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplicationTests.java +++ b/spring-cloud-gateway-integration-tests/mvc-failure-analyzer/src/test/java/org/springframework/cloud/gateway/sample/MvcFailureAnalyzerApplicationTests.java @@ -23,6 +23,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.test.system.CapturedOutput; import org.springframework.boot.test.system.OutputCaptureExtension; import org.springframework.cloud.gateway.support.MvcFoundOnClasspathException; +import org.springframework.cloud.gateway.support.MvcFoundOnClasspathFailureAnalyzer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.web.reactive.server.WebTestClient; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; @@ -40,8 +43,8 @@ public class MvcFailureAnalyzerApplicationTests { () -> new SpringApplication(MvcFailureAnalyzerApplication.class) .run("--server.port=0")).hasRootCauseInstanceOf( MvcFoundOnClasspathException.class); - assertThat(output).contains("Spring MVC found on classpath", - "Please remove spring-boot-starter-web dependency"); + assertThat(output).contains(MvcFoundOnClasspathFailureAnalyzer.MESSAGE, + MvcFoundOnClasspathFailureAnalyzer.ACTION); } @Test @@ -49,8 +52,28 @@ public class MvcFailureAnalyzerApplicationTests { assertThatCode(() -> new SpringApplication(MvcFailureAnalyzerApplication.class) .run("--spring.cloud.gateway.enabled=false", "--server.port=0")) .doesNotThrowAnyException(); - assertThat(output).doesNotContain("Spring MVC found on classpath", - "Please remove spring-boot-starter-web dependency"); + assertThat(output).doesNotContain(MvcFoundOnClasspathFailureAnalyzer.MESSAGE, + MvcFoundOnClasspathFailureAnalyzer.ACTION); + } + + @Test + public void exceptionNotThrownWhenReactiveTypeSet(CapturedOutput output) { + assertThatCode(() -> { + ConfigurableApplicationContext context = new SpringApplication( + MvcFailureAnalyzerApplication.class).run( + "--spring.main.web-application-type=reactive", + "--server.port=0", "--debug=true"); + Integer port = context.getEnvironment().getProperty("local.server.port", + Integer.class); + WebTestClient client = WebTestClient.bindToServer() + .baseUrl("http://localhost:" + port).build(); + client.get().uri("/myprefix/hello").exchange().expectStatus().isOk() + .expectBody(String.class).isEqualTo("Hello"); + context.close(); + }).doesNotThrowAnyException(); + assertThat(output).doesNotContain(MvcFoundOnClasspathFailureAnalyzer.MESSAGE, + MvcFoundOnClasspathFailureAnalyzer.ACTION); + } } diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java index 36b61182..cf780512 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/GatewayClassPathWarningAutoConfiguration.java @@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.cloud.gateway.support.MvcFoundOnClasspathException; import org.springframework.context.annotation.Configuration; @@ -38,6 +39,7 @@ public class GatewayClassPathWarningAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet") + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) protected static class SpringMvcFoundOnClasspathConfiguration { public SpringMvcFoundOnClasspathConfiguration() { diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/MvcFoundOnClasspathFailureAnalyzer.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/MvcFoundOnClasspathFailureAnalyzer.java index 17c8828f..93483e56 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/MvcFoundOnClasspathFailureAnalyzer.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/support/MvcFoundOnClasspathFailureAnalyzer.java @@ -22,12 +22,20 @@ import org.springframework.boot.diagnostics.FailureAnalysis; public class MvcFoundOnClasspathFailureAnalyzer extends AbstractFailureAnalyzer { + /** + * Message for MvcFoundOnClasspathException. + */ + public static final String MESSAGE = "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway."; + + /** + * Action for MvcFoundOnClasspathException. + */ + public static final String ACTION = "Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency."; + @Override protected FailureAnalysis analyze(Throwable rootFailure, MvcFoundOnClasspathException cause) { - String message = "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway."; - String action = "Please remove spring-boot-starter-web dependency."; - return new FailureAnalysis(message, action, cause); + return new FailureAnalysis(MESSAGE, ACTION, cause); } }