Commit 0f652cdf authored by Stephane Nicoll's avatar Stephane Nicoll

Fix injection point with proxied DataSource

Prior to this commit, the `ApplicationContext` couldn't start with a
JDK-proxied `HikariDataSource` as the JMX auto-configuration was
attempting to inject a (too narrowed) `HikariDataSource`.

This commit rather injects a regular `DataSource` and attempt to unwrap
it as a `HikariDataSource`.

Closes gh-12271
parent 5fa71a1f
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -48,23 +48,33 @@ class DataSourceJmxConfiguration { ...@@ -48,23 +48,33 @@ class DataSourceJmxConfiguration {
@Configuration @Configuration
@ConditionalOnClass(HikariDataSource.class) @ConditionalOnClass(HikariDataSource.class)
@ConditionalOnSingleCandidate(HikariDataSource.class) @ConditionalOnSingleCandidate(DataSource.class)
static class Hikari { static class Hikari {
private final HikariDataSource dataSource; private final DataSource dataSource;
private final ObjectProvider<MBeanExporter> mBeanExporter; private final ObjectProvider<MBeanExporter> mBeanExporter;
Hikari(HikariDataSource dataSource, ObjectProvider<MBeanExporter> mBeanExporter) { Hikari(DataSource dataSource, ObjectProvider<MBeanExporter> mBeanExporter) {
this.dataSource = dataSource; this.dataSource = dataSource;
this.mBeanExporter = mBeanExporter; this.mBeanExporter = mBeanExporter;
} }
@PostConstruct @PostConstruct
public void validateMBeans() { public void validateMBeans() {
MBeanExporter exporter = this.mBeanExporter.getIfUnique(); HikariDataSource hikariDataSource = unwrapHikariDataSource();
if (exporter != null && this.dataSource.isRegisterMbeans()) { if (hikariDataSource != null && hikariDataSource.isRegisterMbeans()) {
exporter.addExcludedBean("dataSource"); this.mBeanExporter.ifUnique((exporter) ->
exporter.addExcludedBean("dataSource"));
}
}
private HikariDataSource unwrapHikariDataSource() {
try {
return this.dataSource.unwrap(HikariDataSource.class);
}
catch (SQLException ex) {
return null;
} }
} }
......
...@@ -31,9 +31,13 @@ import org.apache.tomcat.jdbc.pool.DataSourceProxy; ...@@ -31,9 +31,13 @@ import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool; import org.apache.tomcat.jdbc.pool.jmx.ConnectionPool;
import org.junit.Test; import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
...@@ -101,6 +105,24 @@ public class DataSourceJmxConfigurationTests { ...@@ -101,6 +105,24 @@ public class DataSourceJmxConfigurationTests {
}); });
} }
@Test
public void hikariProxiedCanUseRegisterMBeans() {
String poolName = UUID.randomUUID().toString();
this.contextRunner.withUserConfiguration(DataSourceProxyConfiguration.class)
.withPropertyValues(
"spring.datasource.type=" + HikariDataSource.class.getName(),
"spring.datasource.name=" + poolName,
"spring.datasource.hikari.register-mbeans=true")
.run((context) -> {
assertThat(context).hasSingleBean(javax.sql.DataSource.class);
HikariDataSource hikariDataSource = context.getBean(
javax.sql.DataSource.class).unwrap(HikariDataSource.class);
assertThat(hikariDataSource.isRegisterMbeans()).isTrue();
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
validateHikariMBeansRegistration(mBeanServer, poolName, true);
});
}
private void validateHikariMBeansRegistration(MBeanServer mBeanServer, private void validateHikariMBeansRegistration(MBeanServer mBeanServer,
String poolName, boolean expected) throws MalformedObjectNameException { String poolName, boolean expected) throws MalformedObjectNameException {
assertThat(mBeanServer.isRegistered( assertThat(mBeanServer.isRegistered(
...@@ -130,4 +152,31 @@ public class DataSourceJmxConfigurationTests { ...@@ -130,4 +152,31 @@ public class DataSourceJmxConfigurationTests {
}); });
} }
@Configuration
static class DataSourceProxyConfiguration {
@Bean
public static DataSourceBeanPostProcessor dataSourceBeanPostProcessor() {
return new DataSourceBeanPostProcessor();
}
}
private static class DataSourceBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof javax.sql.DataSource) {
return wrap((javax.sql.DataSource) bean);
}
return bean;
}
private static javax.sql.DataSource wrap(javax.sql.DataSource dataSource) {
return (javax.sql.DataSource) new ProxyFactory(dataSource).getProxy();
}
}
} }
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