Browse Source

Polish "Add failure analysis for missing auto-configured MailSender"

See gh-49582

Signed-off-by: Andy Wilkinson <andy.wilkinson@broadcom.com>
pull/49582/head
Andy Wilkinson 5 days ago
parent
commit
f0aff3aa3c
  1. 49
      module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java
  2. 82
      module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java

49
module/spring-boot-mail/src/main/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzer.java

@ -16,18 +16,27 @@ @@ -16,18 +16,27 @@
package org.springframework.boot.mail.autoconfigure;
import java.util.Map;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.mail.autoconfigure.MailSenderAutoConfiguration.MailSenderCondition;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.mail.MailSender;
/**
* An {@link AbstractFailureAnalyzer} that improves missing {@link MailSender} guidance
* when mail auto-configuration is present but not activated.
* when {@link MailSenderAutoConfiguration} is present but did not match.
*
* @author MJY (answndud)
* @author Andy Wilkinson
*/
class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuchBeanDefinitionException>
implements Ordered {
@ -36,18 +45,22 @@ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuch @@ -36,18 +45,22 @@ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuch
private static final String MAIL_JNDI_NAME_PROPERTY = "spring.mail.jndi-name";
private final @Nullable Environment environment;
private final BeanFactory beanFactory;
NoSuchMailSenderBeanFailureAnalyzer(@Nullable Environment environment) {
this.environment = environment;
NoSuchMailSenderBeanFailureAnalyzer(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
protected @Nullable FailureAnalysis analyze(Throwable rootFailure, NoSuchBeanDefinitionException cause) {
if (!isMissingMailSenderBean(cause) || hasMailConfigurationProperty()) {
if (!isMissingMailSenderBean(cause)) {
return null;
}
String description = "A MailSender bean could not be found because Spring Boot mail auto-configuration "
ConditionAndOutcome conditionAndOutcome = findMailSenderConditionOutcome();
if (conditionAndOutcome == null || conditionAndOutcome.getOutcome().isMatch()) {
return null;
}
String description = "A MailSender bean could not be found because MailSenderAutoConfiguration "
+ "did not match. Neither '" + MAIL_HOST_PROPERTY + "' nor '" + MAIL_JNDI_NAME_PROPERTY
+ "' is configured.";
String action = "Consider configuring '" + MAIL_HOST_PROPERTY + "' or '" + MAIL_JNDI_NAME_PROPERTY
@ -56,6 +69,23 @@ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuch @@ -56,6 +69,23 @@ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuch
return new FailureAnalysis(description, action, cause);
}
private @Nullable ConditionAndOutcome findMailSenderConditionOutcome() {
ConditionEvaluationReport conditionEvaluationReport = ConditionEvaluationReport.find(this.beanFactory);
if (conditionEvaluationReport != null) {
Map<String, ConditionAndOutcomes> conditionAndOutcomesBySource = conditionEvaluationReport
.getConditionAndOutcomesBySource();
ConditionAndOutcomes conditionAndOutcomes = conditionAndOutcomesBySource
.get(MailSenderAutoConfiguration.class.getName());
if (conditionAndOutcomes != null) {
return conditionAndOutcomes.stream()
.filter((candidate) -> candidate.getCondition() instanceof MailSenderCondition)
.findFirst()
.orElse(null);
}
}
return null;
}
private boolean isMissingMailSenderBean(NoSuchBeanDefinitionException cause) {
Class<?> beanType = cause.getBeanType();
if (beanType == null && cause.getResolvableType() != null) {
@ -64,11 +94,6 @@ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuch @@ -64,11 +94,6 @@ class NoSuchMailSenderBeanFailureAnalyzer extends AbstractFailureAnalyzer<NoSuch
return (beanType != null) && MailSender.class.isAssignableFrom(beanType);
}
private boolean hasMailConfigurationProperty() {
return this.environment != null && (this.environment.containsProperty(MAIL_HOST_PROPERTY)
|| this.environment.containsProperty(MAIL_JNDI_NAME_PROPERTY));
}
@Override
public int getOrder() {
return 0;

82
module/spring-boot-mail/src/test/java/org/springframework/boot/mail/autoconfigure/NoSuchMailSenderBeanFailureAnalyzerTests.java

@ -18,65 +18,73 @@ package org.springframework.boot.mail.autoconfigure; @@ -18,65 +18,73 @@ package org.springframework.boot.mail.autoconfigure;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.core.env.Environment;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.mail.MailSender;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mock.env.MockEnvironment;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatException;
/**
* Tests for {@link NoSuchMailSenderBeanFailureAnalyzer}.
*
* @author MJY (answndud)
* @author Andy Wilkinson
*/
class NoSuchMailSenderBeanFailureAnalyzerTests {
@Test
void analyzeWhenNotNoSuchBeanDefinitionExceptionShouldReturnNull() {
assertThat(new NoSuchMailSenderBeanFailureAnalyzer(null).analyze(new Exception())).isNull();
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class))
.run((context) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory)
.analyze(new Exception());
assertThat(analysis).isNull();
});
}
@Test
void analyzeWhenNoSuchBeanDefinitionExceptionForDifferentTypeShouldReturnNull() {
assertThat(
new NoSuchMailSenderBeanFailureAnalyzer(null).analyze(new NoSuchBeanDefinitionException(String.class)))
.isNull();
}
@Test
void analyzeWhenMailHostPropertyIsConfiguredShouldReturnNull() {
Environment environment = new MockEnvironment().withProperty("spring.mail.host", "smtp.example.org");
assertThat(new NoSuchMailSenderBeanFailureAnalyzer(environment)
.analyze(new NoSuchBeanDefinitionException(MailSender.class))).isNull();
}
@Test
void analyzeWhenMailJndiNamePropertyIsConfiguredShouldReturnNull() {
Environment environment = new MockEnvironment().withProperty("spring.mail.jndi-name", "mail/Session");
assertThat(new NoSuchMailSenderBeanFailureAnalyzer(environment)
.analyze(new NoSuchBeanDefinitionException(MailSender.class))).isNull();
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class))
.run((context) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
assertThatException().isThrownBy(() -> context.getBean(String.class)).satisfies((ex) -> {
FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory).analyze(ex);
assertThat(analysis).isNull();
});
});
}
@Test
void analyzeWhenMailSenderBeanIsMissingAndNoMailPropertiesAreConfiguredShouldProvideGuidance() {
FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(new MockEnvironment())
.analyze(new NoSuchBeanDefinitionException(MailSender.class));
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription())
.contains("A MailSender bean could not be found")
.contains("spring.mail.host")
.contains("spring.mail.jndi-name");
assertThat(analysis.getAction())
.contains("spring.mail.host")
.contains("spring.mail.jndi-name")
.contains("MailSender bean");
void analyzeWithoutMailSenderAutoConfigurationShouldReturnNull() {
new ApplicationContextRunner().run((context) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
assertThatException().isThrownBy(() -> context.getBean(MailSender.class)).satisfies((ex) -> {
FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory).analyze(ex);
assertThat(analysis).isNull();
});
});
}
@Test
void analyzeWhenJavaMailSenderBeanIsMissingAndNoMailPropertiesAreConfiguredShouldProvideGuidance() {
assertThat(new NoSuchMailSenderBeanFailureAnalyzer(new MockEnvironment())
.analyze(new NoSuchBeanDefinitionException(JavaMailSender.class))).isNotNull();
void analyzeWhenMailSenderBeanIsMissingAndMailSenderConditionDidNotMatchShouldProvideGuidance() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(MailSenderAutoConfiguration.class))
.run((context) -> {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
assertThatException().isThrownBy(() -> context.getBean(MailSender.class)).satisfies((ex) -> {
FailureAnalysis analysis = new NoSuchMailSenderBeanFailureAnalyzer(beanFactory).analyze(ex);
assertThat(analysis).isNotNull();
assertThat(analysis.getDescription()).contains("A MailSender bean could not be found")
.contains("spring.mail.host")
.contains("spring.mail.jndi-name");
assertThat(analysis.getAction()).contains("spring.mail.host")
.contains("spring.mail.jndi-name")
.contains("MailSender bean");
});
});
}
}

Loading…
Cancel
Save