diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java index 6358d0af40..0fdda37d8f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.java @@ -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; + } } /** diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java index 2c7d1f24fd..8ee675dd09 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/TomcatDataSourceConfigurationTests.java @@ -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(); } } diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/sample/data/jpa/SampleDataJpaApplicationTests.java b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/sample/data/jpa/SampleDataJpaApplicationTests.java index 6ca8aabc1b..292c5936b6 100644 --- a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/sample/data/jpa/SampleDataJpaApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/java/sample/data/jpa/SampleDataJpaApplicationTests.java @@ -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()); + } + } diff --git a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties index 78831d76bd..667889c663 100644 --- a/spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties +++ b/spring-boot-samples/spring-boot-sample-data-jpa/src/test/resources/application-scratch.properties @@ -1 +1,2 @@ spring.datasource.url: jdbc:hsqldb:mem:scratchdb +spring.jmx.default_domain: jpa.sample \ No newline at end of file