Refactor JndiPropertySource

Prior to this change, JndiPropertySource worked directly against a JNDI
Context instance as its 'source' object.  This works well enough, but is
not nearly as fully-featured as Spring's existing JndiLocatorDelegate.

This change refactors JndiPropertySource from relying on an underlying
Context to relying on an underlying JndiLocatorDelegate.  By default,
the delegate's "resourceRef" property is set to true, meaning that the
implementation will always try to prepand a given name with
"java:comp/env/" before looking up the name, and upon failure will drop
back to the given name sans prefix.

See JndiPropertySource Javadoc for complete details.

Issue: SPR-8490

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4648 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Chris Beams 2011-07-02 21:26:44 +00:00
parent 3be44b3f97
commit 006da7ed81
2 changed files with 140 additions and 46 deletions

View File

@ -16,97 +16,110 @@
package org.springframework.jndi; package org.springframework.jndi;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException; import javax.naming.NamingException;
import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySource;
/** /**
* {@link PropertySource} implementation that reads properties from a JNDI * {@link PropertySource} implementation that reads properties from an underlying Spring
* {@link Context}. All properties retrieved through {@link #getProperty(String)} will * {@link JndiLocatorDelegate}.
* automatically be prefixed with "java:comp/env/" when executing the actual *
* {@link Context#lookup(String)} call. This default can be overridden using * <p>By default, the underlying {@code JndiLocatorDelegate} will be configured with its
* {@link #setJndiPrefix(String)} property. * {@link JndiLocatorDelegate#setResourceRef(boolean) "resourceRef"} property set to
* {@code true}, meaning that names looked up will automatically be prefixed with
* "java:comp/env/" in alignment with published
* <a href="http://download.oracle.com/javase/jndi/tutorial/beyond/misc/policy.html">JNDI
* naming conventions</a>. To override this setting or to change the prefix, manually
* configure a {@code JndiLocatorDelegate} and provide it to one of the constructors here
* that accepts it. The same applies when providing custom JNDI properties. These should
* be specified using {@link JndiLocatorDelegate#setJndiEnvironment(java.util.Properties)}
* prior to construction of the {@code JndiPropertySource}.
*
* <p>{@link org.springframework.web.context.support.StandardServletEnvironment
* StandardServletEnvironment} allows for declaratively including a
* {@code JndiPropertySource} through its support for a {@link
* org.springframework.web.context.support.StandardServletEnvironment#JNDI_PROPERTY_SOURCE_ENABLED
* JNDI_PROPERTY_SOURCE_ENABLED} context-param, but any customization of the underlying
* {@link JndiLocatorDelegate} will typically be done within an {@link
* org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}
* or {@link org.springframework.web.WebApplicationInitializer WebApplicationInitializer}.
* *
* @author Chris Beams * @author Chris Beams
* @author Juergen Hoeller * @author Juergen Hoeller
* @since 3.1 * @since 3.1
* @see Context#lookup(String) * @see JndiLocatorDelegate
* @see org.springframework.context.ApplicationContextInitializer
* @see org.springframework.web.WebApplicationInitializer
* @see org.springframework.web.context.support.StandardServletEnvironment * @see org.springframework.web.context.support.StandardServletEnvironment
*/ */
public class JndiPropertySource extends PropertySource<Context> { public class JndiPropertySource extends PropertySource<JndiLocatorDelegate> {
/** JNDI context property source name: {@value} */ /** JNDI context property source name: {@value} */
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiPropertySource"; public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiPropertySource";
/** /**
* Create a new {@code JndiPropertySource} with the default name * Create a new {@code JndiPropertySource} with the default name
* {@value #JNDI_PROPERTY_SOURCE_NAME} and create a new {@link InitialContext} * {@value #JNDI_PROPERTY_SOURCE_NAME} and a {@link JndiLocatorDelegate} configured
* as the source object. * to prefix any names with "java:comp/env/".
* @throws JndiLookupFailureException if a new {@link InitialContext}
* cannot be created.
*/ */
public JndiPropertySource() throws JndiLookupFailureException { public JndiPropertySource() {
this(JNDI_PROPERTY_SOURCE_NAME, createInitialContext(null)); this(JNDI_PROPERTY_SOURCE_NAME);
} }
/** /**
* Create a new {@code JndiPropertySource} with the given name and * Create a new {@code JndiPropertySource} with the given name
* create a new {@link InitialContext} as the source object. * and a {@link JndiLocatorDelegate} configured to prefix any names with
* @throws JndiLookupFailureException if a new {@link InitialContext} * "java:comp/env/".
* cannot be created.
*/ */
public JndiPropertySource(String name) throws JndiLookupFailureException { public JndiPropertySource(String name) {
this(name, createInitialContext(null)); this(name, createDefaultJndiLocator());
} }
/** /**
* Create a new {@code JndiPropertySource} with the given name and * Create a new {@code JndiPropertySource} with the default name
* use the given jndiEnvironment properties to create a new * {@value #JNDI_PROPERTY_SOURCE_NAME} and the given {@code JndiLocatorDelegate}.
* {@link InitialContext} as the source object.
* @throws JndiLookupFailureException if a new {@link InitialContext}
* cannot be created.
*/ */
public JndiPropertySource(String name, Properties jndiEnvironment) throws JndiLookupFailureException { public JndiPropertySource(JndiLocatorDelegate jndiLocator) {
this(name, createInitialContext(jndiEnvironment)); this(JNDI_PROPERTY_SOURCE_NAME, jndiLocator);
} }
/** /**
* Create a new {@code JndiPropertySource} with the given name and * Create a new {@code JndiPropertySource} with the given name and the given
* JNDI {@link Context}. * {@code JndiLocatorDelegate}.
*/ */
public JndiPropertySource(String name, Context source) { public JndiPropertySource(String name, JndiLocatorDelegate jndiLocator) {
super(name, source); super(name, jndiLocator);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
* <p>This implementation looks up and returns the given name from the source JNDI * <p>This implementation looks up and returns the value associated with the given
* {@link Context}. If a {@link NamingException} is thrown during the call to * name from the underlying {@link JndiLocatorDelegate}. If a {@link NamingException}
* {@link Context#lookup(String)}, returns {@code null} and issue a DEBUG-level log * is thrown during the call to {@link JndiLocatorDelegate#lookup(String)}, returns
* statement with the exception message. * {@code null} and issues a DEBUG-level log statement with the exception message.
*/ */
@Override @Override
public Object getProperty(String name) { public Object getProperty(String name) {
try { try {
Object value = this.source.lookup(name); Object value = this.source.lookup(name);
logger.debug("Context#lookup(" + name + ") returned: [" + value + "]"); logger.debug("JNDI lookup for name [" + name + "] returned: [" + value + "]");
return value; return value;
} catch (NamingException ex) { } catch (NamingException ex) {
logger.debug("Context#lookup(" + name + ") threw NamingException with message: " + ex.getMessage()); logger.debug("JNDI lookup for name [" + name + "] threw NamingException " +
"with message: " + ex.getMessage() + ". Returning null.");
return null; return null;
} }
} }
private static Context createInitialContext(Properties jndiEnvironment) { /**
try { * Configure a {@code JndiLocatorDelegate} with its "resourceRef" property set to true
return new InitialContext(jndiEnvironment); * meaning that all names will be prefixed with "java:comp/env/".
} catch (NamingException ex) { * @return
throw new JndiLookupFailureException("unable to create InitialContext", ex); */
} private static JndiLocatorDelegate createDefaultJndiLocator() {
JndiLocatorDelegate jndiLocator = new JndiLocatorDelegate();
jndiLocator.setResourceRef(true);
return jndiLocator;
} }
} }

View File

@ -0,0 +1,81 @@
/*
* Copyright 2002-2011 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.jndi;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import javax.naming.Context;
import javax.naming.NamingException;
import org.junit.Test;
import org.springframework.mock.jndi.SimpleNamingContext;
/**
* Unit tests for {@link JndiPropertySource}.
*
* @author Chris Beams
* @since 3.1
*/
public class JndiPropertySourceTests {
@Test
public void nonExistentProperty() {
JndiPropertySource ps = new JndiPropertySource();
assertThat(ps.getProperty("bogus"), nullValue());
}
@Test
public void nameBoundWithoutPrefix() {
final SimpleNamingContext context = new SimpleNamingContext();
context.bind("p1", "v1");
JndiTemplate jndiTemplate = new JndiTemplate() {
@Override
protected Context createInitialContext() throws NamingException {
return context;
}
};
JndiLocatorDelegate jndiLocator = new JndiLocatorDelegate();
jndiLocator.setResourceRef(true);
jndiLocator.setJndiTemplate(jndiTemplate);
JndiPropertySource ps = new JndiPropertySource(jndiLocator);
assertThat((String)ps.getProperty("p1"), equalTo("v1"));
}
@Test
public void nameBoundWithPrefix() {
final SimpleNamingContext context = new SimpleNamingContext();
context.bind("java:comp/env/p1", "v1");
JndiTemplate jndiTemplate = new JndiTemplate() {
@Override
protected Context createInitialContext() throws NamingException {
return context;
}
};
JndiLocatorDelegate jndiLocator = new JndiLocatorDelegate();
jndiLocator.setResourceRef(true);
jndiLocator.setJndiTemplate(jndiTemplate);
JndiPropertySource ps = new JndiPropertySource(jndiLocator);
assertThat((String)ps.getProperty("p1"), equalTo("v1"));
}
}