Add JMX MBean for Tomcat DataSource

If the DataSource is a Tomcat one we force it to register an MBean
if spring.jmx.enabled=true

Fixes gh-1590
This commit is contained in:
Dave Syer 2014-11-03 13:28:26 +00:00
parent 0061f237b8
commit f304d46955
4 changed files with 59 additions and 6 deletions

View File

@ -16,9 +16,14 @@
package org.springframework.boot.autoconfigure.jdbc;
import java.sql.SQLException;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
@ -27,6 +32,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.Registrar;
@ -59,6 +65,8 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ Registrar.class, DataSourcePoolMetadataProvidersConfiguration.class })
public class DataSourceAutoConfiguration {
private static Log logger = LogFactory.getLog(DataSourceAutoConfiguration.class);
/**
* Determines if the {@code dataSource} being used by Spring was created from
@ -134,7 +142,25 @@ public class DataSourceAutoConfiguration {
public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(this.dataSource);
}
}
@Configuration
@ConditionalOnExpression("${spring.datasource.jmxEnabled:true}")
@ConditionalOnClass(name = "org.apache.tomcat.jdbc.pool.DataSourceProxy")
@Conditional(DataSourceAutoConfiguration.DataSourceAvailableCondition.class)
protected static class TomcatDataSourceJmxConfiguration {
@Bean
public Object dataSourceMBean(DataSource dataSource) {
if (dataSource instanceof DataSourceProxy) {
try {
return ((DataSourceProxy) dataSource).createPool().getJmxPool();
}
catch (SQLException e) {
logger.warn("Cannot expose DataSource to JMX (could not connect)");
}
}
return null;
}
}
/**

View File

@ -26,6 +26,7 @@ import org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReport;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -134,11 +135,20 @@ public class TomcatDataSourceConfigurationTests {
@Import(DataSourceAutoConfiguration.class)
protected static class TomcatDataSourceConfiguration {
@Autowired
private DataSourceProperties properties;
@Bean
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
public DataSource dataSource() {
return DataSourceBuilder.create()
.type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
DataSourceBuilder factory = DataSourceBuilder
.create(this.properties.getClassLoader())
.driverClassName(this.properties.getDriverClassName())
.url(this.properties.getUrl())
.username(this.properties.getUsername())
.password(this.properties.getPassword())
.type(org.apache.tomcat.jdbc.pool.DataSource.class);
return factory.build();
}
}

View File

@ -1,29 +1,38 @@
package sample.data.jpa;
import static org.junit.Assert.assertEquals;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.lang.management.ManagementFactory;
import javax.management.ObjectName;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Integration test to run the application.
*
* @author Oliver Gierke
* @author Dave Syer
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleDataJpaApplication.class)
@WebAppConfiguration
// Enable JMX so we can test the MBeans (you can't do this in a properties file)
@TestPropertySource(properties="spring.jmx.enabled:true")
@ActiveProfiles("scratch")
// Separate profile for web tests to avoid clashing databases
public class SampleDataJpaApplicationTests {
@ -44,4 +53,11 @@ public class SampleDataJpaApplicationTests {
this.mvc.perform(get("/")).andExpect(status().isOk())
.andExpect(content().string("Bath"));
}
@Test
public void testJmx() throws Exception {
assertEquals(1, ManagementFactory.getPlatformMBeanServer().queryMBeans(
new ObjectName("jpa.sample:type=ConnectionPool,*"), null).size());
}
}

View File

@ -1 +1,2 @@
spring.datasource.url: jdbc:hsqldb:mem:scratchdb
spring.jmx.default_domain: jpa.sample