diff --git a/spring-boot-actuator/pom.xml b/spring-boot-actuator/pom.xml
index 22d8cb62db4..48a866fda3e 100644
--- a/spring-boot-actuator/pom.xml
+++ b/spring-boot-actuator/pom.xml
@@ -41,6 +41,11 @@
spring-context
+
+ com.sun.mail
+ javax.mail
+ true
+
io.dropwizard.metrics
metrics-core
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java
index c3a3decbf05..eeb56a73343 100644
--- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration.java
@@ -32,6 +32,7 @@ import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
+import org.springframework.boot.actuate.health.MailHealthIndicator;
import org.springframework.boot.actuate.health.MongoHealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
@@ -48,6 +49,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders;
+import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration;
@@ -57,6 +59,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link HealthIndicator}s.
@@ -70,7 +73,8 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
@AutoConfigureBefore({ EndpointAutoConfiguration.class })
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class, RedisAutoConfiguration.class,
- RabbitAutoConfiguration.class, SolrAutoConfiguration.class })
+ RabbitAutoConfiguration.class, SolrAutoConfiguration.class,
+ MailSenderAutoConfiguration.class })
@EnableConfigurationProperties({ HealthIndicatorAutoConfigurationProperties.class })
public class HealthIndicatorAutoConfiguration {
@@ -276,4 +280,40 @@ public class HealthIndicatorAutoConfiguration {
}
+ @Configuration
+ @ConditionalOnBean(JavaMailSenderImpl.class)
+ @ConditionalOnProperty(prefix = "management.health.mail", name = "enabled", matchIfMissing = true)
+ public static class MailHealthIndicatorConfiguration {
+
+ @Autowired
+ private HealthAggregator healthAggregator;
+
+ @Autowired(required = false)
+ private Map mailSenders;
+
+ @Bean
+ @ConditionalOnMissingBean(name = "mailSenderHealthIndicator")
+ public HealthIndicator mailHealthIndicator() {
+ if (this.mailSenders.size() == 1) {
+ JavaMailSenderImpl mailSender = this.mailSenders.values().iterator()
+ .next();
+ return createMailHealthIndicator(mailSender);
+ }
+ CompositeHealthIndicator composite = new CompositeHealthIndicator(
+ this.healthAggregator);
+ for (Map.Entry entry : this.mailSenders
+ .entrySet()) {
+ String name = entry.getKey();
+ JavaMailSenderImpl mailSender = entry.getValue();
+ composite.addHealthIndicator(name, createMailHealthIndicator(mailSender));
+ }
+ return composite;
+ }
+
+ private MailHealthIndicator createMailHealthIndicator(
+ JavaMailSenderImpl mailSender) {
+ return new MailHealthIndicator(mailSender);
+ }
+ }
+
}
diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MailHealthIndicator.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MailHealthIndicator.java
new file mode 100644
index 00000000000..c6d42f42eec
--- /dev/null
+++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/MailHealthIndicator.java
@@ -0,0 +1,86 @@
+/*
+ * 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.actuate.health;
+
+import javax.mail.MessagingException;
+import javax.mail.NoSuchProviderException;
+import javax.mail.Session;
+import javax.mail.Transport;
+
+import org.springframework.boot.actuate.health.Health.Builder;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+
+/**
+ * {@link HealthIndicator} for configured smtp server(s).
+ *
+ * @author Johannes Stelzer
+ * @since 1.3.0
+ */
+public class MailHealthIndicator extends AbstractHealthIndicator {
+
+ private final JavaMailSenderImpl mailSender;
+
+ public MailHealthIndicator(JavaMailSenderImpl mailSender) {
+ this.mailSender = mailSender;
+ }
+
+ @Override
+ protected void doHealthCheck(Builder builder) throws Exception {
+ String location = String.format("%s:%d", this.mailSender.getHost(), this.mailSender.getPort());
+ builder.withDetail("location", location);
+
+ Transport transport = null;
+ try {
+ transport = connectTransport();
+ builder.up();
+ }
+ finally {
+ if (transport != null) {
+ transport.close();
+ }
+ }
+ }
+
+ // Copy-paste from JavaMailSenderImpl - see SPR-12799
+ private Transport connectTransport() throws MessagingException {
+ String username = this.mailSender.getUsername();
+ String password = this.mailSender.getPassword();
+ if ("".equals(username)) {
+ username = null;
+ if ("".equals(password)) {
+ password = null;
+ }
+ }
+
+ Transport transport = getTransport(this.mailSender.getSession());
+ transport.connect(this.mailSender.getHost(), this.mailSender.getPort(), username,
+ password);
+ return transport;
+ }
+
+ private Transport getTransport(Session session) throws NoSuchProviderException {
+ String protocol = this.mailSender.getProtocol();
+ if (protocol == null) {
+ protocol = session.getProperty("mail.transport.protocol");
+ if (protocol == null) {
+ protocol = JavaMailSenderImpl.DEFAULT_PROTOCOL;
+ }
+ }
+ return session.getTransport(protocol);
+ }
+
+}
diff --git a/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index dd95d1badb9..9f4510dd870 100644
--- a/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -58,6 +58,12 @@
"description": "Enable Solr health check.",
"defaultValue": true
},
+ {
+ "name": "management.health.mail.enabled",
+ "type": "java.lang.Boolean",
+ "description": "Enable Mail health check.",
+ "defaultValue": true
+ },
{
"name": "spring.git.properties",
"type": "java.lang.String",
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java
index 56ac8fbb533..c3428977050 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfigurationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * 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.
@@ -27,6 +27,7 @@ import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
+import org.springframework.boot.actuate.health.MailHealthIndicator;
import org.springframework.boot.actuate.health.MongoHealthIndicator;
import org.springframework.boot.actuate.health.RabbitHealthIndicator;
import org.springframework.boot.actuate.health.RedisHealthIndicator;
@@ -37,6 +38,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
+import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration;
@@ -304,4 +306,38 @@ public class HealthIndicatorAutoConfigurationTests {
}
+ @Test
+ public void mailHealthIndicator() {
+ this.context = new AnnotationConfigApplicationContext();
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "spring.mail.host:smtp.acme.org",
+ "management.health.diskspace.enabled:false");
+ this.context.register(MailSenderAutoConfiguration.class,
+ ManagementServerProperties.class, HealthIndicatorAutoConfiguration.class);
+ this.context.refresh();
+
+ Map beans = this.context
+ .getBeansOfType(HealthIndicator.class);
+ assertEquals(1, beans.size());
+ assertEquals(MailHealthIndicator.class, beans.values().iterator().next()
+ .getClass());
+ }
+
+ @Test
+ public void notMailHealthIndicator() {
+ this.context = new AnnotationConfigApplicationContext();
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "spring.mail.host:smtp.acme.org", "management.health.mail.enabled:false",
+ "management.health.diskspace.enabled:false");
+ this.context.register(MailSenderAutoConfiguration.class,
+ ManagementServerProperties.class, HealthIndicatorAutoConfiguration.class);
+ this.context.refresh();
+
+ Map beans = this.context
+ .getBeansOfType(HealthIndicator.class);
+ assertEquals(1, beans.size());
+ assertEquals(ApplicationHealthIndicator.class, beans.values().iterator().next()
+ .getClass());
+ }
+
}
diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MailHealthIndicatorTest.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MailHealthIndicatorTest.java
new file mode 100644
index 00000000000..102c291d62e
--- /dev/null
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/health/MailHealthIndicatorTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.actuate.health;
+
+import java.util.Properties;
+
+import javax.mail.Address;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Provider;
+import javax.mail.Provider.Type;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.URLName;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link MailHealthIndicator}.
+ *
+ * @author Johannes Stelzer
+ */
+public class MailHealthIndicatorTest {
+
+ private JavaMailSenderImpl mailSender;
+ private MailHealthIndicator indicator;
+
+ @Before
+ public void setup() {
+ Session session = Session.getDefaultInstance(new Properties());
+ session.addProvider(new Provider(Type.TRANSPORT, "success",
+ SuccessTransport.class.getName(), "Test", "1.0.0"));
+
+ session.addProvider(new Provider(Type.TRANSPORT, "fail", FailTransport.class
+ .getName(), "Test", "1.0.0"));
+
+ session.addProvider(new Provider(Type.TRANSPORT, "failOnClose",
+ FailOnCloseTransport.class.getName(), "Test", "1.0.0"));
+
+ this.mailSender = mock(JavaMailSenderImpl.class);
+ when(this.mailSender.getHost()).thenReturn("smtp.acme.org");
+ when(this.mailSender.getPort()).thenReturn(25);
+ when(this.mailSender.getSession()).thenReturn(session);
+
+ this.indicator = new MailHealthIndicator(this.mailSender);
+ }
+
+ @Test
+ public void smtpIsUp() {
+ when(this.mailSender.getProtocol()).thenReturn("success");
+
+ Health health = this.indicator.health();
+
+ assertEquals(Status.UP, health.getStatus());
+ assertEquals("smtp.acme.org:25", health.getDetails().get("location"));
+ }
+
+ @Test
+ public void smtpIsDown() {
+ when(this.mailSender.getProtocol()).thenReturn("fail");
+
+ Health health = this.indicator.health();
+
+ assertEquals(Status.DOWN, health.getStatus());
+ assertEquals("smtp.acme.org:25", health.getDetails().get("location"));
+ }
+
+ @Test
+ public void unexpectedExceptionOnClose() {
+ when(this.mailSender.getProtocol()).thenReturn("failOnClose");
+
+ Health health = this.indicator.health();
+
+ assertEquals(Status.DOWN, health.getStatus());
+ assertEquals("smtp.acme.org:25", health.getDetails().get("location"));
+ }
+
+ public static class SuccessTransport extends Transport {
+ public SuccessTransport(Session session, URLName urlname) {
+ super(session, urlname);
+ }
+
+ @Override
+ public synchronized void connect(String host, int port, String user,
+ String password) throws MessagingException {
+ }
+
+ @Override
+ public void sendMessage(Message msg, Address[] addresses)
+ throws MessagingException {
+ }
+
+ }
+
+ public static class FailTransport extends SuccessTransport {
+ public FailTransport(Session session, URLName urlname) {
+ super(session, urlname);
+ }
+
+ @Override
+ public synchronized void connect(String host, int port, String user,
+ String password) throws MessagingException {
+ throw new MessagingException("fail on connect");
+ }
+ }
+
+ public static class FailOnCloseTransport extends SuccessTransport {
+ public FailOnCloseTransport(Session session, URLName urlname) {
+ super(session, urlname);
+ }
+
+ @Override
+ public synchronized void close() throws MessagingException {
+ throw new MessagingException("fail on close");
+ }
+ }
+
+}
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 cafd4b7270e..44aadecd341 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
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2014 the original author or authors.
+ * 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.
@@ -30,7 +30,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.MailSender;
-import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
/**
@@ -38,6 +37,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
*
* @author Oliver Gierke
* @author Stephane Nicoll
+ * @author Johannes Stelzer
* @since 1.2.0
*/
@Configuration
@@ -51,7 +51,7 @@ public class MailSenderAutoConfiguration {
MailProperties properties;
@Bean
- public JavaMailSender mailSender() {
+ public JavaMailSenderImpl mailSender() {
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(this.properties.getHost());
if (this.properties.getPort() != null) {