From dbc59052ba9741aaf1549ee3d344abe0ab7a61a8 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 7 Apr 2022 17:40:58 +0100 Subject: [PATCH] Polish "Add failure analyzer for missing web factory bean" See gh-30358 --- .../MissingWebServerFactoryBeanException.java | 59 +++++++++++++++++++ ...ngWebServerFactoryBeanFailureAnalyzer.java | 32 +++++----- .../ReactiveWebServerApplicationContext.java | 2 +- .../MissingWebServerFactoryBeanException.java | 58 ------------------ .../boot/web/server/context/package-info.java | 21 ------- .../ServletWebServerApplicationContext.java | 2 +- .../main/resources/META-INF/spring.factories | 2 +- ...ServerFactoryBeanFailureAnalyzerTests.java | 19 +++--- 8 files changed, 89 insertions(+), 106 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanException.java rename spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/{server => }/context/MissingWebServerFactoryBeanFailureAnalyzer.java (53%) delete mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanException.java delete mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/package-info.java rename spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/{server => }/context/MissingWebServerFactoryBeanFailureAnalyzerTests.java (71%) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanException.java new file mode 100644 index 0000000000..5585fc49d4 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanException.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.web.context; + +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.web.server.WebServerFactory; + +/** + * Exception thrown when there is no {@link WebServerFactory} bean of the required type + * defined in a {@link WebServerApplicationContext}. + * + * @author Guirong Hu + * @author Andy Wilkinson + * @since 2.7.0 + */ +public class MissingWebServerFactoryBeanException extends NoSuchBeanDefinitionException { + + private final WebApplicationType webApplicationType; + + /** + * Create a new {@code MissingWebServerFactoryBeanException}. + * @param webServerApplicationContextClass the class of the + * WebServerApplicationContext that required the WebServerFactory + * @param webServerFactoryClass the class of the WebServerFactory that was missing + * @param webApplicationType the type of the web application + */ + public MissingWebServerFactoryBeanException( + Class webServerApplicationContextClass, + Class webServerFactoryClass, WebApplicationType webApplicationType) { + super(webServerFactoryClass, String.format("Unable to start %s due to missing %s bean", + webServerApplicationContextClass.getSimpleName(), webServerFactoryClass.getSimpleName())); + this.webApplicationType = webApplicationType; + } + + /** + * Returns the type of web application for which a {@link WebServerFactory} bean was + * missing. + * @return the type of web application + */ + public WebApplicationType getWebApplicationType() { + return this.webApplicationType; + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanFailureAnalyzer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanFailureAnalyzer.java similarity index 53% rename from spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanFailureAnalyzer.java rename to spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanFailureAnalyzer.java index 164a8c7d19..bea77b2cfb 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanFailureAnalyzer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanFailureAnalyzer.java @@ -14,36 +14,34 @@ * limitations under the License. */ -package org.springframework.boot.web.server.context; +package org.springframework.boot.web.context; + +import java.util.Locale; -import org.springframework.boot.WebApplicationType; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.diagnostics.FailureAnalyzer; -import org.springframework.context.ApplicationContextException; +import org.springframework.core.annotation.Order; /** * A {@link FailureAnalyzer} that performs analysis of failures caused by an * {@link MissingWebServerFactoryBeanException}. * * @author Guirong Hu + * @author Andy Wilkinson */ -class MissingWebServerFactoryBeanFailureAnalyzer extends AbstractFailureAnalyzer { - - private static final String ACTION = "Check your application's dependencies on supported web servers " - + "or configuration of web application type."; +@Order(0) +class MissingWebServerFactoryBeanFailureAnalyzer extends AbstractFailureAnalyzer { @Override - protected FailureAnalysis analyze(Throwable rootFailure, ApplicationContextException cause) { - Throwable rootCause = cause.getCause(); - if (rootCause instanceof MissingWebServerFactoryBeanException) { - WebApplicationType webApplicationType = ((MissingWebServerFactoryBeanException) rootCause) - .getWebApplicationType(); - return new FailureAnalysis(String.format( - "Reason: The running web application is of type %s, but the dependent class is missing.", - webApplicationType.name().toLowerCase()), ACTION, cause); - } - return null; + protected FailureAnalysis analyze(Throwable rootFailure, MissingWebServerFactoryBeanException cause) { + return new FailureAnalysis( + "Web application could not be started as there was no " + cause.getBeanType().getName() + + " bean defined in the context.", + "Check your application's dependencies for a supported " + + cause.getWebApplicationType().name().toLowerCase(Locale.ENGLISH) + " web server.\n" + + "Check the configured web application type.", + cause); } } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java index e028c84cd1..8871149c10 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/ReactiveWebServerApplicationContext.java @@ -22,10 +22,10 @@ import org.springframework.boot.WebApplicationType; import org.springframework.boot.availability.AvailabilityChangeEvent; import org.springframework.boot.availability.ReadinessState; import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext; +import org.springframework.boot.web.context.MissingWebServerFactoryBeanException; import org.springframework.boot.web.context.WebServerGracefulShutdownLifecycle; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.boot.web.server.WebServer; -import org.springframework.boot.web.server.context.MissingWebServerFactoryBeanException; import org.springframework.context.ApplicationContextException; import org.springframework.core.metrics.StartupStep; import org.springframework.http.server.reactive.HttpHandler; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanException.java deleted file mode 100644 index cd9a0707da..0000000000 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanException.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.web.server.context; - -import org.springframework.boot.WebApplicationType; -import org.springframework.context.ApplicationContextException; -import org.springframework.lang.NonNull; - -/** - * Throws exception when web server factory bean is missing. - * - * @author Guirong Hu - * @since 2.7.0 - */ -@SuppressWarnings("serial") -public class MissingWebServerFactoryBeanException extends ApplicationContextException { - - private final WebApplicationType webApplicationType; - - /** - * Create a new {@code MissingWebServerFactoryBeanException} with the given web - * application context class and the given web server factory class and the given type - * of web application. - * @param webApplicationContextClass the web application context class - * @param webServerFactoryClass the web server factory class - * @param webApplicationType the type of web application - */ - public MissingWebServerFactoryBeanException(@NonNull Class webApplicationContextClass, - @NonNull Class webServerFactoryClass, @NonNull WebApplicationType webApplicationType) { - super(String.format("Unable to start %s due to missing %s bean.", webApplicationContextClass.getSimpleName(), - webServerFactoryClass.getSimpleName())); - this.webApplicationType = webApplicationType; - } - - /** - * Returns the type of web application that is being run. - * @return the type of web application - * @since 2.7.0 - */ - public WebApplicationType getWebApplicationType() { - return this.webApplicationType; - } - -} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/package-info.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/package-info.java deleted file mode 100644 index 8381001256..0000000000 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/context/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Web server integrations with Spring's - * {@link org.springframework.context.ApplicationContext ApplicationContext}. - */ -package org.springframework.boot.web.server.context; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java index c2315b6540..0fcb28d5f5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext.java @@ -41,9 +41,9 @@ import org.springframework.boot.WebApplicationType; import org.springframework.boot.availability.AvailabilityChangeEvent; import org.springframework.boot.availability.ReadinessState; import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext; +import org.springframework.boot.web.context.MissingWebServerFactoryBeanException; import org.springframework.boot.web.context.WebServerGracefulShutdownLifecycle; import org.springframework.boot.web.server.WebServer; -import org.springframework.boot.web.server.context.MissingWebServerFactoryBeanException; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.boot.web.servlet.ServletContextInitializerBeans; diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories index 4de66425d1..6480da116e 100644 --- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories @@ -74,8 +74,8 @@ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFa org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer,\ org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer,\ +org.springframework.boot.web.context.MissingWebServerFactoryBeanFailureAnalyzer,\ org.springframework.boot.web.embedded.tomcat.ConnectorStartFailureAnalyzer,\ -org.springframework.boot.web.server.context.MissingWebServerFactoryBeanFailureAnalyzer # Failure Analysis Reporters org.springframework.boot.diagnostics.FailureAnalysisReporter=\ diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanFailureAnalyzerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanFailureAnalyzerTests.java similarity index 71% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanFailureAnalyzerTests.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanFailureAnalyzerTests.java index 2583f468b3..fb30be29ca 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/server/context/MissingWebServerFactoryBeanFailureAnalyzerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/context/MissingWebServerFactoryBeanFailureAnalyzerTests.java @@ -14,13 +14,15 @@ * limitations under the License. */ -package org.springframework.boot.web.server.context; +package org.springframework.boot.web.context; import org.junit.jupiter.api.Test; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext; +import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext; +import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.ApplicationContextException; import org.springframework.context.ConfigurableApplicationContext; @@ -30,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link MissingWebServerFactoryBeanFailureAnalyzer}. * * @author Guirong Hu + * @author Andy Wilkinson */ class MissingWebServerFactoryBeanFailureAnalyzerTests { @@ -39,10 +42,11 @@ class MissingWebServerFactoryBeanFailureAnalyzerTests { assertThat(failure).isNotNull(); FailureAnalysis analysis = new MissingWebServerFactoryBeanFailureAnalyzer().analyze(failure); assertThat(analysis).isNotNull(); - assertThat(analysis.getDescription()).isEqualTo( - "Reason: The running web application is of type servlet, but the dependent class is missing."); + assertThat(analysis.getDescription()).isEqualTo("Web application could not be started as there was no " + + ServletWebServerFactory.class.getName() + " bean defined in the context."); assertThat(analysis.getAction()).isEqualTo( - "Check your application's dependencies on supported web servers or configuration of web application type."); + "Check your application's dependencies for a supported servlet web server.\nCheck the configured web " + + "application type."); } @Test @@ -50,10 +54,11 @@ class MissingWebServerFactoryBeanFailureAnalyzerTests { ApplicationContextException failure = createFailure(new ReactiveWebServerApplicationContext()); FailureAnalysis analysis = new MissingWebServerFactoryBeanFailureAnalyzer().analyze(failure); assertThat(analysis).isNotNull(); - assertThat(analysis.getDescription()).isEqualTo( - "Reason: The running web application is of type reactive, but the dependent class is missing."); + assertThat(analysis.getDescription()).isEqualTo("Web application could not be started as there was no " + + ReactiveWebServerFactory.class.getName() + " bean defined in the context."); assertThat(analysis.getAction()).isEqualTo( - "Check your application's dependencies on supported web servers or configuration of web application type."); + "Check your application's dependencies for a supported reactive web server.\nCheck the configured web " + + "application type."); } private ApplicationContextException createFailure(ConfigurableApplicationContext context) {