From 6f31080f78a688592efbafd281fb1019a0293029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 2 Jul 2015 13:41:17 -0500 Subject: [PATCH 1/2] Add mail server connection check If a `JavaMailSenderImpl` is available, check that the underlying mail server is available on startup. Add a `spring.mail.test-connection` property to control this behaviour. Closes gh-3408 --- .../boot/autoconfigure/mail/MailProperties.java | 12 ++++++++++++ .../mail/MailSenderAutoConfiguration.java | 13 +++++++++++++ .../asciidoc/appendix-application-properties.adoc | 1 + 3 files changed, 26 insertions(+) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java index 5349a52a51..2d461c6b28 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java @@ -67,6 +67,11 @@ public class MailProperties { */ private String jndiName; + /** + * Ping to mail server while initializing. + */ + private boolean testConnection; + public String getHost() { return this.host; } @@ -119,4 +124,11 @@ public class MailProperties { return this.jndiName; } + public boolean isTestConnection() { + return this.testConnection; + } + + public void setTestConnection(boolean testConnection) { + this.testConnection = testConnection; + } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java index 640a39635f..1d90961acd 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Properties; import javax.activation.MimeType; +import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.MimeMessage; @@ -69,9 +70,21 @@ public class MailSenderAutoConfiguration { else { applyProperties(sender); } + validateConnection(sender); return sender; } + private void validateConnection(JavaMailSenderImpl sender) { + if (this.properties.isTestConnection()) { + try { + sender.testConnection(); + } catch (MessagingException ex) { + throw new IllegalStateException( + String.format("Unable to ping to %s", this.properties.getHost()), ex); + } + } + } + private void applyProperties(JavaMailSenderImpl sender) { sender.setHost(this.properties.getHost()); if (this.properties.getPort() != null) { diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 235545b4fa..74cf441ab0 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -519,6 +519,7 @@ content into your application; rather pick only the properties that you need. spring.mail.default-encoding=UTF-8 # encoding to use for MimeMessages spring.mail.properties.*= # properties to set on the JavaMail session spring.mail.jndi-name= # JNDI location of a Mail Session + spring.mail.test-connection= # Enable test connection. Default: false # SPRING BATCH ({sc-spring-boot-autoconfigure}/batch/BatchProperties.{sc-ext}[BatchProperties]) spring.batch.job.names=job1,job2 From eb10275f5a3734b718ec018d8d5f4cdc1152008c Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 6 Jul 2015 14:38:28 +0200 Subject: [PATCH 2/2] Polish Polish 383e94c to validate any `JavaMailSenderImpl` and not only the one that has been created by auto-configuration. Closes gh-3409 --- .../mail/JndiSessionConfiguration.java | 60 ------- .../autoconfigure/mail/MailProperties.java | 2 +- .../mail/MailSenderAutoConfiguration.java | 151 +++++++++++------- .../MailSenderAutoConfigurationTests.java | 33 +++- .../appendix-application-properties.adoc | 2 +- 5 files changed, 129 insertions(+), 119 deletions(-) delete mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/JndiSessionConfiguration.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/JndiSessionConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/JndiSessionConfiguration.java deleted file mode 100644 index cf851058e9..0000000000 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/JndiSessionConfiguration.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012-2015 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 - * - * http://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.autoconfigure.mail; - -import javax.mail.Session; -import javax.naming.NamingException; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.jndi.JndiLocatorDelegate; - -/** - * Auto-configure a {@link Session} available on JNDI. - * - * @author EddĂș MelĂ©ndez - * @author Stephane Nicoll - * @since 1.3.0 - */ -@Configuration -@ConditionalOnClass(Session.class) -@ConditionalOnProperty(prefix = "spring.mail", name = "jndi-name") -@ConditionalOnJndi -class JndiSessionConfiguration { - - @Autowired - private MailProperties properties; - - @Bean - @ConditionalOnMissingBean - public Session session() { - String jndiName = this.properties.getJndiName(); - try { - return new JndiLocatorDelegate().lookup(jndiName, Session.class); - } - catch (NamingException ex) { - throw new IllegalStateException(String.format( - "Unable to find Session in JNDI location %s", jndiName), ex); - } - } - -} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java index 2d461c6b28..f07e758f2a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailProperties.java @@ -68,7 +68,7 @@ public class MailProperties { private String jndiName; /** - * Ping to mail server while initializing. + * Test that the mail server is available on startup. */ private boolean testConnection; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java index 1d90961acd..0deecc5133 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfiguration.java @@ -18,24 +18,26 @@ package org.springframework.boot.autoconfigure.mail; import java.util.Map; import java.util.Properties; - import javax.activation.MimeType; +import javax.annotation.PostConstruct; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.MimeMessage; +import javax.naming.NamingException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnJndi; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration.MailSenderCondition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; +import org.springframework.jndi.JndiLocatorDelegate; import org.springframework.mail.MailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; @@ -49,79 +51,116 @@ import org.springframework.mail.javamail.JavaMailSenderImpl; */ @Configuration @ConditionalOnClass({ MimeMessage.class, MimeType.class }) -@ConditionalOnMissingBean(MailSender.class) -@Conditional(MailSenderCondition.class) @EnableConfigurationProperties(MailProperties.class) -@Import(JndiSessionConfiguration.class) public class MailSenderAutoConfiguration { - @Autowired(required = false) - private Session session; + @Configuration + @ConditionalOnClass(Session.class) + @ConditionalOnProperty(prefix = "spring.mail", name = "jndi-name") + @ConditionalOnJndi + static class JndiSessionConfiguration { - @Autowired - private MailProperties properties; + @Autowired + private MailProperties properties; - @Bean - public JavaMailSenderImpl mailSender() { - JavaMailSenderImpl sender = new JavaMailSenderImpl(); - if (this.session != null) { - sender.setSession(this.session); - } - else { - applyProperties(sender); + @Bean + @ConditionalOnMissingBean + public Session session() { + String jndiName = this.properties.getJndiName(); + try { + return new JndiLocatorDelegate().lookup(jndiName, Session.class); + } + catch (NamingException ex) { + throw new IllegalStateException(String.format( + "Unable to find Session in JNDI location %s", jndiName), ex); + } } - validateConnection(sender); - return sender; + } - private void validateConnection(JavaMailSenderImpl sender) { - if (this.properties.isTestConnection()) { - try { - sender.testConnection(); - } catch (MessagingException ex) { - throw new IllegalStateException( - String.format("Unable to ping to %s", this.properties.getHost()), ex); + @ConditionalOnMissingBean(MailSender.class) + @Conditional(MailSenderConfiguration.MailSenderCondition.class) + static class MailSenderConfiguration { + + @Autowired + private MailProperties properties; + + @Autowired(required = false) + private Session session; + + @Bean + public JavaMailSenderImpl mailSender() { + JavaMailSenderImpl sender = new JavaMailSenderImpl(); + if (this.session != null) { + sender.setSession(this.session); } + else { + applyProperties(sender); + } + return sender; } - } - private void applyProperties(JavaMailSenderImpl sender) { - sender.setHost(this.properties.getHost()); - if (this.properties.getPort() != null) { - sender.setPort(this.properties.getPort()); + private void applyProperties(JavaMailSenderImpl sender) { + sender.setHost(this.properties.getHost()); + if (this.properties.getPort() != null) { + sender.setPort(this.properties.getPort()); + } + sender.setUsername(this.properties.getUsername()); + sender.setPassword(this.properties.getPassword()); + sender.setDefaultEncoding(this.properties.getDefaultEncoding()); + if (!this.properties.getProperties().isEmpty()) { + sender.setJavaMailProperties(asProperties(this.properties.getProperties())); + } } - sender.setUsername(this.properties.getUsername()); - sender.setPassword(this.properties.getPassword()); - sender.setDefaultEncoding(this.properties.getDefaultEncoding()); - if (!this.properties.getProperties().isEmpty()) { - sender.setJavaMailProperties(asProperties(this.properties.getProperties())); + + private Properties asProperties(Map source) { + Properties properties = new Properties(); + properties.putAll(source); + return properties; } - } - private Properties asProperties(Map source) { - Properties properties = new Properties(); - properties.putAll(source); - return properties; - } + /** + * Condition to trigger the creation of a {@link JavaMailSenderImpl}. This kicks in if + * either the host or jndi name property is set. + */ + static class MailSenderCondition extends AnyNestedCondition { - /** - * Condition to trigger the creation of a {@link JavaMailSenderImpl}. This kicks in if - * either the host or jndi name property is set. - */ - static class MailSenderCondition extends AnyNestedCondition { + public MailSenderCondition() { + super(ConfigurationPhase.PARSE_CONFIGURATION); + } - public MailSenderCondition() { - super(ConfigurationPhase.PARSE_CONFIGURATION); - } + @ConditionalOnProperty(prefix = "spring.mail", name = "host") + static class HostProperty { + } - @ConditionalOnProperty(prefix = "spring.mail", name = "host") - static class HostProperty { - } + @ConditionalOnProperty(prefix = "spring.mail", name = "jndi-name") + static class JndiNameProperty { + } - @ConditionalOnProperty(prefix = "spring.mail", name = "jndi-name") - static class JndiNameProperty { } + } + @Configuration + @ConditionalOnSingleCandidate(JavaMailSenderImpl.class) + static class MailSenderValidator { + + @Autowired + private MailProperties properties; + + @Autowired + private JavaMailSenderImpl mailSender; + + @PostConstruct + public void validateConnection() { + if (this.properties.isTestConnection()) { + try { + this.mailSender.testConnection(); + } + catch (MessagingException ex) { + throw new IllegalStateException("Mail server is not unavailable", ex); + } + } + } } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationTests.java index acde4df4f5..c471ac80db 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mail/MailSenderAutoConfigurationTests.java @@ -17,7 +17,7 @@ package org.springframework.boot.autoconfigure.mail; import java.util.Properties; - +import javax.mail.MessagingException; import javax.mail.Session; import javax.naming.Context; import javax.naming.NamingException; @@ -40,6 +40,10 @@ import org.springframework.mail.javamail.JavaMailSenderImpl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; /** * Tests for {@link MailSenderAutoConfiguration}. @@ -170,6 +174,22 @@ public class MailSenderAutoConfigurationTests { load(EmptyConfig.class, "spring.mail.jndi-name:foo"); } + @Test + public void connectionOnStartup() throws MessagingException { + load(MockMailConfiguration.class, "spring.mail.host:10.0.0.23", + "spring.mail.test-connection:true"); + JavaMailSenderImpl mailSender = this.context.getBean(JavaMailSenderImpl.class); + verify(mailSender, times(1)).testConnection(); + } + + @Test + public void connectionOnStartupNotCalled() throws MessagingException { + load(MockMailConfiguration.class, "spring.mail.host:10.0.0.23", + "spring.mail.test-connection:false"); + JavaMailSenderImpl mailSender = this.context.getBean(JavaMailSenderImpl.class); + verify(mailSender, never()).testConnection(); + } + private Session configureJndiSession(String name) throws IllegalStateException, NamingException { Properties properties = new Properties(); @@ -207,4 +227,15 @@ public class MailSenderAutoConfigurationTests { } + @Configuration + static class MockMailConfiguration { + + @Bean + JavaMailSenderImpl mockMailSender() { + return mock(JavaMailSenderImpl.class); + } + + } + + } diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 74cf441ab0..3335f984c5 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -519,7 +519,7 @@ content into your application; rather pick only the properties that you need. spring.mail.default-encoding=UTF-8 # encoding to use for MimeMessages spring.mail.properties.*= # properties to set on the JavaMail session spring.mail.jndi-name= # JNDI location of a Mail Session - spring.mail.test-connection= # Enable test connection. Default: false + spring.mail.test-connection=false # Test that the mail server is available on startup # SPRING BATCH ({sc-spring-boot-autoconfigure}/batch/BatchProperties.{sc-ext}[BatchProperties]) spring.batch.job.names=job1,job2