Skip to content

Commit 5922cfa

Browse files
committed
Avoid use of SimpleNamingContextBuilder as it pollutes JVM’s JNDI config
Closes gh-2397
1 parent 82d1e61 commit 5922cfa

File tree

4 files changed

+178
-109
lines changed

4 files changed

+178
-109
lines changed

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndiTests.java

Lines changed: 2 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,17 @@
1616

1717
package org.springframework.boot.autoconfigure.condition;
1818

19-
import java.io.IOException;
20-
import java.net.URL;
21-
import java.util.Collections;
22-
import java.util.Enumeration;
2319
import java.util.HashMap;
24-
import java.util.Hashtable;
2520
import java.util.Map;
2621

2722
import javax.naming.Context;
28-
import javax.naming.InitialContext;
29-
import javax.naming.NamingException;
30-
import javax.naming.spi.InitialContextFactory;
3123

3224
import org.hamcrest.Matcher;
3325
import org.junit.After;
3426
import org.junit.Before;
3527
import org.junit.Test;
28+
import org.springframework.boot.autoconfigure.jndi.JndiPropertiesHidingClassLoader;
29+
import org.springframework.boot.autoconfigure.jndi.TestableInitialContextFactory;
3630
import org.springframework.boot.test.EnvironmentTestUtils;
3731
import org.springframework.context.ConfigurableApplicationContext;
3832
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -206,92 +200,4 @@ public void setFoundLocation(String foundLocation) {
206200
}
207201
}
208202

209-
public static class TestableInitialContextFactory implements InitialContextFactory {
210-
211-
private static TestableContext context;
212-
213-
@Override
214-
public Context getInitialContext(Hashtable<?, ?> environment)
215-
throws NamingException {
216-
return getContext();
217-
}
218-
219-
public static void bind(String name, Object obj) {
220-
try {
221-
getContext().bind(name, obj);
222-
}
223-
catch (NamingException ex) {
224-
throw new IllegalStateException(ex);
225-
}
226-
}
227-
228-
public static void clearAll() {
229-
getContext().clearAll();
230-
}
231-
232-
private static TestableContext getContext() {
233-
if (context == null) {
234-
try {
235-
context = new TestableContext();
236-
}
237-
catch (NamingException ex) {
238-
throw new IllegalStateException(ex);
239-
}
240-
}
241-
return context;
242-
}
243-
244-
private static class TestableContext extends InitialContext {
245-
246-
private final Map<String, Object> bindings = new HashMap<String, Object>();
247-
248-
private TestableContext() throws NamingException {
249-
super(true);
250-
}
251-
252-
@Override
253-
public void bind(String name, Object obj) throws NamingException {
254-
this.bindings.put(name, obj);
255-
}
256-
257-
@Override
258-
public Object lookup(String name) throws NamingException {
259-
return this.bindings.get(name);
260-
}
261-
262-
@Override
263-
public Hashtable<?, ?> getEnvironment() throws NamingException {
264-
return new Hashtable<Object, Object>(); // Used to detect if JNDI is
265-
// available
266-
}
267-
268-
public void clearAll() {
269-
this.bindings.clear();
270-
}
271-
}
272-
}
273-
274-
/**
275-
* Used as the thread context classloader to prevent jndi.properties resources found
276-
* on the classpath from triggering configuration of an InitialContextFactory that is
277-
* outside the control of these tests.
278-
*/
279-
private static class JndiPropertiesHidingClassLoader extends ClassLoader {
280-
281-
public JndiPropertiesHidingClassLoader(ClassLoader parent) {
282-
super(parent);
283-
}
284-
285-
@Override
286-
public Enumeration<URL> getResources(String name) throws IOException {
287-
if ("jndi.properties".equals(name)) {
288-
return Collections.enumeration(Collections.<URL> emptyList());
289-
}
290-
else {
291-
return super.getResources(name);
292-
}
293-
}
294-
295-
}
296-
297203
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfigurationTests.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,21 @@
1818

1919
import java.util.Set;
2020

21+
import javax.naming.Context;
2122
import javax.naming.NamingException;
2223
import javax.sql.DataSource;
2324

2425
import org.apache.commons.dbcp2.BasicDataSource;
2526
import org.junit.After;
27+
import org.junit.Before;
2628
import org.junit.Test;
2729
import org.springframework.beans.DirectFieldAccessor;
30+
import org.springframework.boot.autoconfigure.jndi.JndiPropertiesHidingClassLoader;
31+
import org.springframework.boot.autoconfigure.jndi.TestableInitialContextFactory;
2832
import org.springframework.boot.test.EnvironmentTestUtils;
2933
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3034
import org.springframework.context.annotation.Bean;
3135
import org.springframework.jmx.export.MBeanExporter;
32-
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
3336

3437
import static org.hamcrest.Matchers.contains;
3538
import static org.hamcrest.Matchers.hasSize;
@@ -43,25 +46,47 @@
4346
*/
4447
public class JndiDataSourceAutoConfigurationTests {
4548

49+
private ClassLoader threadContextClassLoader;
50+
51+
private String initialContextFactory;
52+
4653
private AnnotationConfigApplicationContext context;
4754

48-
private SimpleNamingContextBuilder jndi;
55+
@Before
56+
public void setupJndi() {
57+
this.initialContextFactory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY);
58+
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
59+
TestableInitialContextFactory.class.getName());
60+
}
61+
62+
@Before
63+
public void setupThreadContextClassLoader() {
64+
this.threadContextClassLoader = Thread.currentThread().getContextClassLoader();
65+
Thread.currentThread().setContextClassLoader(
66+
new JndiPropertiesHidingClassLoader(getClass().getClassLoader()));
67+
}
4968

5069
@After
51-
public void cleanup() {
52-
if (this.jndi != null) {
53-
this.jndi.clear();
70+
public void close() {
71+
TestableInitialContextFactory.clearAll();
72+
if (this.initialContextFactory != null) {
73+
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
74+
this.initialContextFactory);
75+
}
76+
else {
77+
System.clearProperty(Context.INITIAL_CONTEXT_FACTORY);
5478
}
5579
if (this.context != null) {
5680
this.context.close();
5781
}
82+
Thread.currentThread().setContextClassLoader(this.threadContextClassLoader);
5883
}
5984

6085
@Test
6186
public void dataSourceIsAvailableFromJndi() throws IllegalStateException,
6287
NamingException {
6388
DataSource dataSource = new BasicDataSource();
64-
this.jndi = configureJndi("foo", dataSource);
89+
configureJndi("foo", dataSource);
6590

6691
this.context = new AnnotationConfigApplicationContext();
6792
EnvironmentTestUtils.addEnvironment(this.context,
@@ -77,7 +102,7 @@ public void dataSourceIsAvailableFromJndi() throws IllegalStateException,
77102
public void mbeanDataSourceIsExcludedFromExport() throws IllegalStateException,
78103
NamingException {
79104
DataSource dataSource = new BasicDataSource();
80-
this.jndi = configureJndi("foo", dataSource);
105+
configureJndi("foo", dataSource);
81106

82107
this.context = new AnnotationConfigApplicationContext();
83108
EnvironmentTestUtils.addEnvironment(this.context,
@@ -98,7 +123,7 @@ public void mbeanDataSourceIsExcludedFromExport() throws IllegalStateException,
98123
public void standardDataSourceIsNotExcludedFromExport() throws IllegalStateException,
99124
NamingException {
100125
DataSource dataSource = new org.apache.commons.dbcp.BasicDataSource();
101-
this.jndi = configureJndi("foo", dataSource);
126+
configureJndi("foo", dataSource);
102127

103128
this.context = new AnnotationConfigApplicationContext();
104129
EnvironmentTestUtils.addEnvironment(this.context,
@@ -114,12 +139,9 @@ public void standardDataSourceIsNotExcludedFromExport() throws IllegalStateExcep
114139
assertThat(excludedBeans, hasSize(0));
115140
}
116141

117-
private SimpleNamingContextBuilder configureJndi(String name, DataSource dataSource)
142+
private void configureJndi(String name, DataSource dataSource)
118143
throws IllegalStateException, NamingException {
119-
SimpleNamingContextBuilder builder = SimpleNamingContextBuilder
120-
.emptyActivatedContextBuilder();
121-
builder.bind(name, dataSource);
122-
return builder;
144+
TestableInitialContextFactory.bind(name, dataSource);
123145
}
124146

125147
private static class MBeanExporterConfiguration {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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.jndi;
18+
19+
import java.io.IOException;
20+
import java.net.URL;
21+
import java.util.Collections;
22+
import java.util.Enumeration;
23+
24+
/**
25+
* Used as the thread context classloader to prevent {@code jndi.properties} resources
26+
* found on the classpath from triggering configuration of an InitialContextFactory.
27+
*
28+
* @author Andy Wilkinson
29+
*/
30+
public class JndiPropertiesHidingClassLoader extends ClassLoader {
31+
32+
public JndiPropertiesHidingClassLoader(ClassLoader parent) {
33+
super(parent);
34+
}
35+
36+
@Override
37+
public Enumeration<URL> getResources(String name) throws IOException {
38+
if ("jndi.properties".equals(name)) {
39+
return Collections.enumeration(Collections.<URL> emptyList());
40+
}
41+
else {
42+
return super.getResources(name);
43+
}
44+
}
45+
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.jndi;
18+
19+
import java.util.HashMap;
20+
import java.util.Hashtable;
21+
import java.util.Map;
22+
23+
import javax.naming.Context;
24+
import javax.naming.InitialContext;
25+
import javax.naming.NamingException;
26+
import javax.naming.spi.InitialContextFactory;
27+
28+
/**
29+
* An {@code InitialContextFactory} implementation to be used for testing JNDI.
30+
*
31+
* @author Stephane Nicoll
32+
*/
33+
public class TestableInitialContextFactory implements InitialContextFactory {
34+
35+
private static TestableContext context;
36+
37+
@Override
38+
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
39+
return getContext();
40+
}
41+
42+
public static void bind(String name, Object obj) {
43+
try {
44+
getContext().bind(name, obj);
45+
}
46+
catch (NamingException ex) {
47+
throw new IllegalStateException(ex);
48+
}
49+
}
50+
51+
public static void clearAll() {
52+
getContext().clearAll();
53+
}
54+
55+
private static TestableContext getContext() {
56+
if (context == null) {
57+
try {
58+
context = new TestableContext();
59+
}
60+
catch (NamingException ex) {
61+
throw new IllegalStateException(ex);
62+
}
63+
}
64+
return context;
65+
}
66+
67+
private static class TestableContext extends InitialContext {
68+
69+
private final Map<String, Object> bindings = new HashMap<String, Object>();
70+
71+
private TestableContext() throws NamingException {
72+
super(true);
73+
}
74+
75+
@Override
76+
public void bind(String name, Object obj) throws NamingException {
77+
this.bindings.put(name, obj);
78+
}
79+
80+
@Override
81+
public Object lookup(String name) throws NamingException {
82+
return this.bindings.get(name);
83+
}
84+
85+
@Override
86+
public Hashtable<?, ?> getEnvironment() throws NamingException {
87+
return new Hashtable<Object, Object>(); // Used to detect if JNDI is
88+
// available
89+
}
90+
91+
public void clearAll() {
92+
this.bindings.clear();
93+
}
94+
}
95+
}

0 commit comments

Comments
 (0)