Polish "Perform best effort to retrieve DataSourceProxy"

Closes gh-15206
This commit is contained in:
Stephane Nicoll
2018-11-23 18:34:33 +01:00
parent 26f9a92837
commit e424dfbe15
5 changed files with 271 additions and 120 deletions

View File

@@ -26,23 +26,20 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.jdbc.DataSourceUnwrapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DelegatingDataSource;
import org.springframework.jmx.export.MBeanExporter;
/**
* Configures DataSource related MBeans.
*
* @author Stephane Nicoll
* @author Tadaya Tsuyukubo
*/
@Configuration
@ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true", matchIfMissing = true)
@@ -93,7 +90,8 @@ class DataSourceJmxConfiguration {
@Bean
@ConditionalOnMissingBean(name = "dataSourceMBean")
public Object dataSourceMBean(DataSource dataSource) {
DataSourceProxy dataSourceProxy = extractDataSourceProxy(dataSource);
DataSourceProxy dataSourceProxy = DataSourceUnwrapper.unwrap(dataSource,
DataSourceProxy.class);
if (dataSourceProxy != null) {
try {
return dataSourceProxy.createPool().getJmxPool();
@@ -105,36 +103,6 @@ class DataSourceJmxConfiguration {
return null;
}
/**
* Perform best effort to retrieve tomcat's {@link DataSourceProxy}.
*
* Since {@link DataSourceProxy#unwrap(Class)} always return {@code null}, it
* cannot directly retrieve {@link DataSourceProxy}. This method tries best effort
* to find {@link DataSourceProxy} if the given {@link DataSource} is wrapped or
* proxied by spring.
* @param dataSource candidate datasource
* @return found DataSourceProxy or null
*/
private DataSourceProxy extractDataSourceProxy(DataSource dataSource) {
if (dataSource instanceof DataSourceProxy) {
return (DataSourceProxy) dataSource; // found
}
else if (dataSource instanceof DelegatingDataSource) {
// check delegating target
return extractDataSourceProxy(
((DelegatingDataSource) dataSource).getTargetDataSource());
}
else if (AopUtils.isAopProxy(dataSource)) {
// for proxy by spring, try target(advised) instance
Object target = AopProxyUtils.getSingletonTarget(dataSource);
if (target instanceof DataSource) {
return extractDataSourceProxy((DataSource) target);
}
}
return null;
}
}
}

View File

@@ -31,9 +31,7 @@ import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool;
import org.junit.Test;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
@@ -159,6 +157,7 @@ public class DataSourceJmxConfigurationTests {
this.contextRunner.withPropertyValues(
"spring.datasource.type=" + DataSource.class.getName(),
"spring.datasource.jmx-enabled=true").run((context) -> {
assertThat(context).hasBean("dataSourceMBean");
assertThat(context).hasSingleBean(ConnectionPool.class);
assertThat(context.getBean(DataSourceProxy.class).createPool()
.getJmxPool())
@@ -173,12 +172,9 @@ public class DataSourceJmxConfigurationTests {
"spring.datasource.type=" + DataSource.class.getName(),
"spring.datasource.jmx-enabled=true")
.run((context) -> {
assertThat(context).hasSingleBean(ConnectionPool.class);
DataSourceProxy dataSourceProxy = (DataSourceProxy) AopProxyUtils
.getSingletonTarget(
context.getBean(javax.sql.DataSource.class));
assertThat(dataSourceProxy.createPool().getJmxPool())
.isSameAs(context.getBean(ConnectionPool.class));
assertThat(context).hasBean("dataSourceMBean");
assertThat(context).getBean("dataSourceMBean")
.isInstanceOf(ConnectionPool.class);
});
}
@@ -189,55 +185,12 @@ public class DataSourceJmxConfigurationTests {
"spring.datasource.type=" + DataSource.class.getName(),
"spring.datasource.jmx-enabled=true")
.run((context) -> {
assertThat(context).hasSingleBean(ConnectionPool.class);
DataSourceProxy dataSourceProxy = (DataSourceProxy) context
.getBean(DelegatingDataSource.class).getTargetDataSource();
assertThat(dataSourceProxy.createPool().getJmxPool())
.isSameAs(context.getBean(ConnectionPool.class));
assertThat(context).hasBean("dataSourceMBean");
assertThat(context).getBean("dataSourceMBean")
.isInstanceOf(ConnectionPool.class);
});
}
@Test
public void tomcatProxyAndDelegateCanExposeMBeanPool() {
this.contextRunner
.withUserConfiguration(DataSourceMixWrapAndProxyConfiguration.class)
.withPropertyValues(
"spring.datasource.type=" + DataSource.class.getName(),
"spring.datasource.jmx-enabled=true")
.run((context) -> {
assertThat(context).hasSingleBean(ConnectionPool.class);
DataSourceProxy dataSourceProxy = extractTomcatDataSource(
context.getBean(javax.sql.DataSource.class));
assertThat(dataSourceProxy.createPool().getJmxPool())
.isSameAs(context.getBean(ConnectionPool.class));
});
}
private static javax.sql.DataSource wrap(javax.sql.DataSource dataSource) {
return (javax.sql.DataSource) new ProxyFactory(dataSource).getProxy();
}
private static javax.sql.DataSource delegate(javax.sql.DataSource dataSource) {
return new DelegatingDataSource(dataSource);
}
private static DataSource extractTomcatDataSource(javax.sql.DataSource dataSource) {
if (dataSource instanceof DataSource) {
return (DataSource) dataSource;
}
else if (dataSource instanceof DelegatingDataSource) {
return extractTomcatDataSource(
((DelegatingDataSource) dataSource).getTargetDataSource());
}
else if (AopUtils.isAopProxy(dataSource)) {
return extractTomcatDataSource(
(javax.sql.DataSource) AopProxyUtils.getSingletonTarget(dataSource));
}
throw new RuntimeException(
"Not proxied or delegated tomcat DataSource: " + dataSource);
}
@Configuration
static class DataSourceProxyConfiguration {
@@ -253,7 +206,7 @@ public class DataSourceJmxConfigurationTests {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof javax.sql.DataSource) {
return wrap((javax.sql.DataSource) bean);
return new ProxyFactory(bean).getProxy();
}
return bean;
}
@@ -279,34 +232,4 @@ public class DataSourceJmxConfigurationTests {
}
@Configuration
static class DataSourceMixWrapAndProxyConfiguration {
@Bean
public static DataSourceBeanPostProcessor dataSourceBeanPostProcessor() {
return new DataSourceBeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean,
String beanName) {
if (bean instanceof javax.sql.DataSource) {
javax.sql.DataSource dataSource = (javax.sql.DataSource) bean;
// delegate/wrap multiple times
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
dataSource = wrap(dataSource);
}
else {
dataSource = delegate(dataSource);
}
}
return dataSource;
}
return bean;
}
};
}
}
}