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 6dfc1e05..0539dc86 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 8d07fb77..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; @@ -36,18 +39,41 @@ public class MvcFailureAnalyzerApplicationTests { @Test public void exceptionThrown(CapturedOutput output) { - assertThatThrownBy(() -> 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"); + assertThatThrownBy( + () -> new SpringApplication(MvcFailureAnalyzerApplication.class) + .run("--server.port=0")).hasRootCauseInstanceOf( + MvcFoundOnClasspathException.class); + assertThat(output).contains(MvcFoundOnClasspathFailureAnalyzer.MESSAGE, + MvcFoundOnClasspathFailureAnalyzer.ACTION); } @Test public void exceptionNotThrownWhenDisabled(CapturedOutput output) { 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"); + .run("--spring.cloud.gateway.enabled=false", "--server.port=0")) + .doesNotThrowAnyException(); + 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 ddb7a98f..2fd3e441 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; @@ -37,6 +38,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 5a0cb97a..c0d5b41f 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 @@ -21,11 +21,19 @@ 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); } }