From 5922cfa41f7a9cfdbbe9eba7385bce161b7c6ad4 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 9 Feb 2015 13:44:30 +0000 Subject: [PATCH] =?UTF-8?q?Avoid=20use=20of=20SimpleNamingContextBuilder?= =?UTF-8?q?=20as=20it=20pollutes=20JVM=E2=80=99s=20JNDI=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes gh-2397 --- .../condition/ConditionalOnJndiTests.java | 98 +------------------ .../JndiDataSourceAutoConfigurationTests.java | 48 ++++++--- .../jndi/JndiPropertiesHidingClassLoader.java | 46 +++++++++ .../jndi/TestableInitialContextFactory.java | 95 ++++++++++++++++++ 4 files changed, 178 insertions(+), 109 deletions(-) create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/JndiPropertiesHidingClassLoader.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/TestableInitialContextFactory.java diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndiTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndiTests.java index 1049479127..239cddb0b9 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndiTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/ConditionalOnJndiTests.java @@ -16,23 +16,17 @@ package org.springframework.boot.autoconfigure.condition; -import java.io.IOException; -import java.net.URL; -import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; -import java.util.Hashtable; import java.util.Map; import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.naming.spi.InitialContextFactory; import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.springframework.boot.autoconfigure.jndi.JndiPropertiesHidingClassLoader; +import org.springframework.boot.autoconfigure.jndi.TestableInitialContextFactory; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -206,92 +200,4 @@ public class ConditionalOnJndiTests { } } - public static class TestableInitialContextFactory implements InitialContextFactory { - - private static TestableContext context; - - @Override - public Context getInitialContext(Hashtable environment) - throws NamingException { - return getContext(); - } - - public static void bind(String name, Object obj) { - try { - getContext().bind(name, obj); - } - catch (NamingException ex) { - throw new IllegalStateException(ex); - } - } - - public static void clearAll() { - getContext().clearAll(); - } - - private static TestableContext getContext() { - if (context == null) { - try { - context = new TestableContext(); - } - catch (NamingException ex) { - throw new IllegalStateException(ex); - } - } - return context; - } - - private static class TestableContext extends InitialContext { - - private final Map bindings = new HashMap(); - - private TestableContext() throws NamingException { - super(true); - } - - @Override - public void bind(String name, Object obj) throws NamingException { - this.bindings.put(name, obj); - } - - @Override - public Object lookup(String name) throws NamingException { - return this.bindings.get(name); - } - - @Override - public Hashtable getEnvironment() throws NamingException { - return new Hashtable(); // Used to detect if JNDI is - // available - } - - public void clearAll() { - this.bindings.clear(); - } - } - } - - /** - * Used as the thread context classloader to prevent jndi.properties resources found - * on the classpath from triggering configuration of an InitialContextFactory that is - * outside the control of these tests. - */ - private static class JndiPropertiesHidingClassLoader extends ClassLoader { - - public JndiPropertiesHidingClassLoader(ClassLoader parent) { - super(parent); - } - - @Override - public Enumeration getResources(String name) throws IOException { - if ("jndi.properties".equals(name)) { - return Collections.enumeration(Collections. emptyList()); - } - else { - return super.getResources(name); - } - } - - } - } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfigurationTests.java index 30ada4b97c..ae0ae15629 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfigurationTests.java @@ -18,18 +18,21 @@ package org.springframework.boot.autoconfigure.jdbc; import java.util.Set; +import javax.naming.Context; import javax.naming.NamingException; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.boot.autoconfigure.jndi.JndiPropertiesHidingClassLoader; +import org.springframework.boot.autoconfigure.jndi.TestableInitialContextFactory; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.jmx.export.MBeanExporter; -import org.springframework.mock.jndi.SimpleNamingContextBuilder; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasSize; @@ -43,25 +46,47 @@ import static org.junit.Assert.assertThat; */ public class JndiDataSourceAutoConfigurationTests { + private ClassLoader threadContextClassLoader; + + private String initialContextFactory; + private AnnotationConfigApplicationContext context; - private SimpleNamingContextBuilder jndi; + @Before + public void setupJndi() { + this.initialContextFactory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); + System.setProperty(Context.INITIAL_CONTEXT_FACTORY, + TestableInitialContextFactory.class.getName()); + } + + @Before + public void setupThreadContextClassLoader() { + this.threadContextClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader( + new JndiPropertiesHidingClassLoader(getClass().getClassLoader())); + } @After - public void cleanup() { - if (this.jndi != null) { - this.jndi.clear(); + public void close() { + TestableInitialContextFactory.clearAll(); + if (this.initialContextFactory != null) { + System.setProperty(Context.INITIAL_CONTEXT_FACTORY, + this.initialContextFactory); + } + else { + System.clearProperty(Context.INITIAL_CONTEXT_FACTORY); } if (this.context != null) { this.context.close(); } + Thread.currentThread().setContextClassLoader(this.threadContextClassLoader); } @Test public void dataSourceIsAvailableFromJndi() throws IllegalStateException, NamingException { DataSource dataSource = new BasicDataSource(); - this.jndi = configureJndi("foo", dataSource); + configureJndi("foo", dataSource); this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, @@ -77,7 +102,7 @@ public class JndiDataSourceAutoConfigurationTests { public void mbeanDataSourceIsExcludedFromExport() throws IllegalStateException, NamingException { DataSource dataSource = new BasicDataSource(); - this.jndi = configureJndi("foo", dataSource); + configureJndi("foo", dataSource); this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, @@ -98,7 +123,7 @@ public class JndiDataSourceAutoConfigurationTests { public void standardDataSourceIsNotExcludedFromExport() throws IllegalStateException, NamingException { DataSource dataSource = new org.apache.commons.dbcp.BasicDataSource(); - this.jndi = configureJndi("foo", dataSource); + configureJndi("foo", dataSource); this.context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(this.context, @@ -114,12 +139,9 @@ public class JndiDataSourceAutoConfigurationTests { assertThat(excludedBeans, hasSize(0)); } - private SimpleNamingContextBuilder configureJndi(String name, DataSource dataSource) + private void configureJndi(String name, DataSource dataSource) throws IllegalStateException, NamingException { - SimpleNamingContextBuilder builder = SimpleNamingContextBuilder - .emptyActivatedContextBuilder(); - builder.bind(name, dataSource); - return builder; + TestableInitialContextFactory.bind(name, dataSource); } private static class MBeanExporterConfiguration { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/JndiPropertiesHidingClassLoader.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/JndiPropertiesHidingClassLoader.java new file mode 100644 index 0000000000..30f70fc429 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/JndiPropertiesHidingClassLoader.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jndi; + +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; + +/** + * Used as the thread context classloader to prevent {@code jndi.properties} resources + * found on the classpath from triggering configuration of an InitialContextFactory. + * + * @author Andy Wilkinson + */ +public class JndiPropertiesHidingClassLoader extends ClassLoader { + + public JndiPropertiesHidingClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + public Enumeration getResources(String name) throws IOException { + if ("jndi.properties".equals(name)) { + return Collections.enumeration(Collections. emptyList()); + } + else { + return super.getResources(name); + } + } + +} \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/TestableInitialContextFactory.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/TestableInitialContextFactory.java new file mode 100644 index 0000000000..c16844a229 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jndi/TestableInitialContextFactory.java @@ -0,0 +1,95 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.jndi; + +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.naming.spi.InitialContextFactory; + +/** + * An {@code InitialContextFactory} implementation to be used for testing JNDI. + * + * @author Stephane Nicoll + */ +public class TestableInitialContextFactory implements InitialContextFactory { + + private static TestableContext context; + + @Override + public Context getInitialContext(Hashtable environment) throws NamingException { + return getContext(); + } + + public static void bind(String name, Object obj) { + try { + getContext().bind(name, obj); + } + catch (NamingException ex) { + throw new IllegalStateException(ex); + } + } + + public static void clearAll() { + getContext().clearAll(); + } + + private static TestableContext getContext() { + if (context == null) { + try { + context = new TestableContext(); + } + catch (NamingException ex) { + throw new IllegalStateException(ex); + } + } + return context; + } + + private static class TestableContext extends InitialContext { + + private final Map bindings = new HashMap(); + + private TestableContext() throws NamingException { + super(true); + } + + @Override + public void bind(String name, Object obj) throws NamingException { + this.bindings.put(name, obj); + } + + @Override + public Object lookup(String name) throws NamingException { + return this.bindings.get(name); + } + + @Override + public Hashtable getEnvironment() throws NamingException { + return new Hashtable(); // Used to detect if JNDI is + // available + } + + public void clearAll() { + this.bindings.clear(); + } + } +} \ No newline at end of file