Commit bfee98e1 authored by Stephane Nicoll's avatar Stephane Nicoll

Add JMS health indicator

Define an additional health indicator for each ConnectionFactory instance
defined in the context. Extracts the provider name from the connection
meta-data.

Fixes gh-2016
parent 9094706f
...@@ -56,6 +56,11 @@ ...@@ -56,6 +56,11 @@
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-broker</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.hibernate</groupId> <groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId> <artifactId>hibernate-validator</artifactId>
......
...@@ -19,10 +19,11 @@ package org.springframework.boot.actuate.autoconfigure; ...@@ -19,10 +19,11 @@ package org.springframework.boot.actuate.autoconfigure;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import javax.jms.ConnectionFactory;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.SolrServer;
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.ApplicationHealthIndicator; import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
...@@ -32,6 +33,7 @@ import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator; ...@@ -32,6 +33,7 @@ import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties; import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.JmsHealthIndicator;
import org.springframework.boot.actuate.health.MailHealthIndicator; import org.springframework.boot.actuate.health.MailHealthIndicator;
import org.springframework.boot.actuate.health.MongoHealthIndicator; import org.springframework.boot.actuate.health.MongoHealthIndicator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator; import org.springframework.boot.actuate.health.OrderedHealthAggregator;
...@@ -49,6 +51,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; ...@@ -49,6 +51,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProviders;
import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration;
import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
...@@ -74,7 +77,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl; ...@@ -74,7 +77,7 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MongoAutoConfiguration.class, @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class, RedisAutoConfiguration.class, MongoDataAutoConfiguration.class, RedisAutoConfiguration.class,
RabbitAutoConfiguration.class, SolrAutoConfiguration.class, RabbitAutoConfiguration.class, SolrAutoConfiguration.class,
MailSenderAutoConfiguration.class }) MailSenderAutoConfiguration.class, JmsAutoConfiguration.class})
@EnableConfigurationProperties({ HealthIndicatorAutoConfigurationProperties.class }) @EnableConfigurationProperties({ HealthIndicatorAutoConfigurationProperties.class })
public class HealthIndicatorAutoConfiguration { public class HealthIndicatorAutoConfiguration {
...@@ -316,4 +319,40 @@ public class HealthIndicatorAutoConfiguration { ...@@ -316,4 +319,40 @@ public class HealthIndicatorAutoConfiguration {
} }
} }
@Configuration
@ConditionalOnBean(ConnectionFactory.class)
@ConditionalOnProperty(prefix = "management.health.jms", name = "enabled", matchIfMissing = true)
public static class JmsHealthIndicatorConfiguration {
@Autowired
private HealthAggregator healthAggregator;
@Autowired(required = false)
private Map<String, ConnectionFactory> connectionFactories;
@Bean
@ConditionalOnMissingBean(name = "jmsHealthIndicator")
public HealthIndicator jmsHealthIndicator() {
if (this.connectionFactories.size() == 1) {
ConnectionFactory connectionFactory = this.connectionFactories.values()
.iterator().next();
return createJmsHealthIndicator(connectionFactory);
}
CompositeHealthIndicator composite = new CompositeHealthIndicator(
this.healthAggregator);
for (Map.Entry<String, ConnectionFactory> entry : this.connectionFactories
.entrySet()) {
String name = entry.getKey();
ConnectionFactory connectionFactory = entry.getValue();
composite.addHealthIndicator(name, createJmsHealthIndicator(connectionFactory));
}
return composite;
}
private JmsHealthIndicator createJmsHealthIndicator(
ConnectionFactory connectionFactory) {
return new JmsHealthIndicator(connectionFactory);
}
}
} }
/*
* 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.jms.Connection;
import javax.jms.ConnectionFactory;
/**
* {@link HealthIndicator} for a JMS {@link ConnectionFactory}.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
public class JmsHealthIndicator extends AbstractHealthIndicator {
private final ConnectionFactory connectionFactory;
public JmsHealthIndicator(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
Connection conToClose = null;
try {
conToClose = this.connectionFactory.createConnection();
builder.up().withDetail("provider",
conToClose.getMetaData().getJMSProviderName());
} finally {
if (conToClose != null) {
conToClose.close();
}
}
}
}
...@@ -34,6 +34,12 @@ ...@@ -34,6 +34,12 @@
"description": "Enable disk space health check.", "description": "Enable disk space health check.",
"defaultValue": true "defaultValue": true
}, },
{
"name": "management.health.jms.enabled",
"type": "java.lang.Boolean",
"description": "Enable JMS health check.",
"defaultValue": true
},
{ {
"name": "management.health.mongo.enabled", "name": "management.health.mongo.enabled",
"type": "java.lang.Boolean", "type": "java.lang.Boolean",
......
...@@ -27,6 +27,7 @@ import org.springframework.boot.actuate.health.ApplicationHealthIndicator; ...@@ -27,6 +27,7 @@ import org.springframework.boot.actuate.health.ApplicationHealthIndicator;
import org.springframework.boot.actuate.health.DataSourceHealthIndicator; import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator; import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.JmsHealthIndicator;
import org.springframework.boot.actuate.health.MailHealthIndicator; import org.springframework.boot.actuate.health.MailHealthIndicator;
import org.springframework.boot.actuate.health.MongoHealthIndicator; import org.springframework.boot.actuate.health.MongoHealthIndicator;
import org.springframework.boot.actuate.health.RabbitHealthIndicator; import org.springframework.boot.actuate.health.RabbitHealthIndicator;
...@@ -38,6 +39,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; ...@@ -38,6 +39,7 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration; import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration;
import org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration;
import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration; import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoDataAutoConfiguration;
...@@ -56,6 +58,7 @@ import static org.junit.Assert.assertEquals; ...@@ -56,6 +58,7 @@ import static org.junit.Assert.assertEquals;
* Tests for {@link HealthIndicatorAutoConfiguration}. * Tests for {@link HealthIndicatorAutoConfiguration}.
* *
* @author Christian Dupuis * @author Christian Dupuis
* @author Stephane Nicoll
*/ */
public class HealthIndicatorAutoConfigurationTests { public class HealthIndicatorAutoConfigurationTests {
...@@ -340,4 +343,37 @@ public class HealthIndicatorAutoConfigurationTests { ...@@ -340,4 +343,37 @@ public class HealthIndicatorAutoConfigurationTests {
.getClass()); .getClass());
} }
@Test
public void jmsHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"management.health.diskspace.enabled:false");
this.context.register(ActiveMQAutoConfiguration.class,
ManagementServerProperties.class, HealthIndicatorAutoConfiguration.class);
this.context.refresh();
Map<String, HealthIndicator> beans = this.context
.getBeansOfType(HealthIndicator.class);
assertEquals(1, beans.size());
assertEquals(JmsHealthIndicator.class, beans.values().iterator().next()
.getClass());
}
@Test
public void notJmsHealthIndicator() {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context,
"management.health.jms.enabled:false",
"management.health.diskspace.enabled:false");
this.context.register(ActiveMQAutoConfiguration.class,
ManagementServerProperties.class, HealthIndicatorAutoConfiguration.class);
this.context.refresh();
Map<String, HealthIndicator> beans = this.context
.getBeansOfType(HealthIndicator.class);
assertEquals(1, beans.size());
assertEquals(ApplicationHealthIndicator.class, beans.values().iterator().next()
.getClass());
}
} }
/*
* 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.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.ConnectionMetaData;
import javax.jms.JMSException;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for {@link JmsHealthIndicator}.
*
* @author Stephane Nicoll
*/
public class JmsHealthIndicatorTests {
@Test
public void jmsBrokerIsUp() throws JMSException {
ConnectionMetaData connectionMetaData = mock(ConnectionMetaData.class);
when(connectionMetaData.getJMSProviderName()).thenReturn("JMS test provider");
Connection connection = mock(Connection.class);
when(connection.getMetaData()).thenReturn(connectionMetaData);
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
when(connectionFactory.createConnection()).thenReturn(connection);
JmsHealthIndicator indicator = new JmsHealthIndicator(connectionFactory);
Health health = indicator.health();
assertEquals(Status.UP, health.getStatus());
assertEquals("JMS test provider", health.getDetails().get("provider"));
verify(connection, times(1)).close();
}
@Test
public void jmsBrokerIsDown() throws JMSException {
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
when(connectionFactory.createConnection()).thenThrow(new JMSException("test", "123"));
JmsHealthIndicator indicator = new JmsHealthIndicator(connectionFactory);
Health health = indicator.health();
assertEquals(Status.DOWN, health.getStatus());
assertEquals(null, health.getDetails().get("provider"));
}
@Test
public void jmsBrokerCouldNotRetrieveProviderMetadata() throws JMSException {
ConnectionMetaData connectionMetaData = mock(ConnectionMetaData.class);
when(connectionMetaData.getJMSProviderName()).thenThrow(new JMSException("test", "123"));
Connection connection = mock(Connection.class);
when(connection.getMetaData()).thenReturn(connectionMetaData);
ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
when(connectionFactory.createConnection()).thenReturn(connection);
JmsHealthIndicator indicator = new JmsHealthIndicator(connectionFactory);
Health health = indicator.health();
assertEquals(Status.DOWN, health.getStatus());
assertEquals(null, health.getDetails().get("provider"));
verify(connection, times(1)).close();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment