Skip to content

Commit 82d1e61

Browse files
committed
Prevent attempt to export MBean for DataSource retreived from JNDI
Tomcat 8's default DataSource implemention is an MBean. Previously, when such a DataSource was consumed from JNDI and an MBeanExporter had been auto-configured an attempt would be made to export this MBean to the MBean server. This would fail due to Tomcat having already registered the MBean. This commit updates JndiDataSourceAutoConfiguration to instruct the MBeanExporter (if there is one) not to export a DataSource MBean that's been retrieved from JNDI. The assumption is that any MBean in JNDI will have already been registered with the MBean server by the same infrastructure that registered it with JNDI. Fixes gh-2397
1 parent b40f689 commit 82d1e61

File tree

2 files changed

+150
-2
lines changed

2 files changed

+150
-2
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2014 the original author or authors.
2+
* Copyright 2012-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
1818

1919
import javax.sql.DataSource;
2020

21+
import org.springframework.beans.factory.annotation.Autowired;
2122
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2223
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2324
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -28,12 +29,15 @@
2829
import org.springframework.context.annotation.Configuration;
2930
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
3031
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
32+
import org.springframework.jmx.export.MBeanExporter;
33+
import org.springframework.jmx.support.JmxUtils;
3134

3235
/**
3336
* {@link EnableAutoConfiguration Auto-configuration} for a JNDI located
3437
* {@link DataSource}.
3538
*
3639
* @author Phillip Webb
40+
* @author Andy Wilkinson
3741
* @since 1.2.0
3842
*/
3943
@Configuration
@@ -44,11 +48,22 @@
4448
@EnableConfigurationProperties(DataSourceProperties.class)
4549
public class JndiDataSourceAutoConfiguration {
4650

51+
@Autowired(required = false)
52+
private MBeanExporter mbeanExporter;
53+
4754
@Bean(destroyMethod = "")
4855
@ConditionalOnMissingBean
4956
public DataSource dataSource(DataSourceProperties properties) {
5057
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
51-
return dataSourceLookup.getDataSource(properties.getJndiName());
58+
DataSource dataSource = dataSourceLookup.getDataSource(properties.getJndiName());
59+
excludeMBeanIfNecessary(dataSource, "dataSource");
60+
return dataSource;
61+
}
62+
63+
private void excludeMBeanIfNecessary(Object candidate, String beanName) {
64+
if (this.mbeanExporter != null && JmxUtils.isMBean(candidate.getClass())) {
65+
this.mbeanExporter.addExcludedBean(beanName);
66+
}
5267
}
5368

5469
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jdbc;
18+
19+
import java.util.Set;
20+
21+
import javax.naming.NamingException;
22+
import javax.sql.DataSource;
23+
24+
import org.apache.commons.dbcp2.BasicDataSource;
25+
import org.junit.After;
26+
import org.junit.Test;
27+
import org.springframework.beans.DirectFieldAccessor;
28+
import org.springframework.boot.test.EnvironmentTestUtils;
29+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.jmx.export.MBeanExporter;
32+
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
33+
34+
import static org.hamcrest.Matchers.contains;
35+
import static org.hamcrest.Matchers.hasSize;
36+
import static org.junit.Assert.assertEquals;
37+
import static org.junit.Assert.assertThat;
38+
39+
/**
40+
* Tests for {@link JndiDataSourceAutoConfiguration}
41+
*
42+
* @author Andy Wilkinson
43+
*/
44+
public class JndiDataSourceAutoConfigurationTests {
45+
46+
private AnnotationConfigApplicationContext context;
47+
48+
private SimpleNamingContextBuilder jndi;
49+
50+
@After
51+
public void cleanup() {
52+
if (this.jndi != null) {
53+
this.jndi.clear();
54+
}
55+
if (this.context != null) {
56+
this.context.close();
57+
}
58+
}
59+
60+
@Test
61+
public void dataSourceIsAvailableFromJndi() throws IllegalStateException,
62+
NamingException {
63+
DataSource dataSource = new BasicDataSource();
64+
this.jndi = configureJndi("foo", dataSource);
65+
66+
this.context = new AnnotationConfigApplicationContext();
67+
EnvironmentTestUtils.addEnvironment(this.context,
68+
"spring.datasource.jndi-name:foo");
69+
this.context.register(JndiDataSourceAutoConfiguration.class);
70+
this.context.refresh();
71+
72+
assertEquals(dataSource, this.context.getBean(DataSource.class));
73+
}
74+
75+
@SuppressWarnings("unchecked")
76+
@Test
77+
public void mbeanDataSourceIsExcludedFromExport() throws IllegalStateException,
78+
NamingException {
79+
DataSource dataSource = new BasicDataSource();
80+
this.jndi = configureJndi("foo", dataSource);
81+
82+
this.context = new AnnotationConfigApplicationContext();
83+
EnvironmentTestUtils.addEnvironment(this.context,
84+
"spring.datasource.jndi-name:foo");
85+
this.context.register(JndiDataSourceAutoConfiguration.class,
86+
MBeanExporterConfiguration.class);
87+
this.context.refresh();
88+
89+
assertEquals(dataSource, this.context.getBean(DataSource.class));
90+
MBeanExporter exporter = this.context.getBean(MBeanExporter.class);
91+
Set<String> excludedBeans = (Set<String>) new DirectFieldAccessor(exporter)
92+
.getPropertyValue("excludedBeans");
93+
assertThat(excludedBeans, contains("dataSource"));
94+
}
95+
96+
@SuppressWarnings("unchecked")
97+
@Test
98+
public void standardDataSourceIsNotExcludedFromExport() throws IllegalStateException,
99+
NamingException {
100+
DataSource dataSource = new org.apache.commons.dbcp.BasicDataSource();
101+
this.jndi = configureJndi("foo", dataSource);
102+
103+
this.context = new AnnotationConfigApplicationContext();
104+
EnvironmentTestUtils.addEnvironment(this.context,
105+
"spring.datasource.jndi-name:foo");
106+
this.context.register(JndiDataSourceAutoConfiguration.class,
107+
MBeanExporterConfiguration.class);
108+
this.context.refresh();
109+
110+
assertEquals(dataSource, this.context.getBean(DataSource.class));
111+
MBeanExporter exporter = this.context.getBean(MBeanExporter.class);
112+
Set<String> excludedBeans = (Set<String>) new DirectFieldAccessor(exporter)
113+
.getPropertyValue("excludedBeans");
114+
assertThat(excludedBeans, hasSize(0));
115+
}
116+
117+
private SimpleNamingContextBuilder configureJndi(String name, DataSource dataSource)
118+
throws IllegalStateException, NamingException {
119+
SimpleNamingContextBuilder builder = SimpleNamingContextBuilder
120+
.emptyActivatedContextBuilder();
121+
builder.bind(name, dataSource);
122+
return builder;
123+
}
124+
125+
private static class MBeanExporterConfiguration {
126+
127+
@Bean
128+
MBeanExporter mbeanExporter() {
129+
return new MBeanExporter();
130+
}
131+
}
132+
133+
}

0 commit comments

Comments
 (0)