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
This commit is contained in:
Chris Beams 2011-07-02 21:26:44 +00:00
parent ce0a0ff3d4
commit d9ee958d88
2 changed files with 140 additions and 46 deletions

View File

@ -16,97 +16,110 @@
package org.springframework.jndi;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.springframework.core.env.PropertySource;
/**
* {@link PropertySource} implementation that reads properties from a JNDI
* {@link Context}. All properties retrieved through {@link #getProperty(String)} will
* automatically be prefixed with "java:comp/env/" when executing the actual
* {@link Context#lookup(String)} call. This default can be overridden using
* {@link #setJndiPrefix(String)} property.
* {@link PropertySource} implementation that reads properties from an underlying Spring
* {@link JndiLocatorDelegate}.
*
* <p>By default, the underlying {@code JndiLocatorDelegate} will be configured with its
* {@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 Juergen Hoeller
* @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
*/
public class JndiPropertySource extends PropertySource<Context> {
public class JndiPropertySource extends PropertySource<JndiLocatorDelegate> {
/** JNDI context property source name: {@value} */
public static final String JNDI_PROPERTY_SOURCE_NAME = "jndiPropertySource";
/**
* Create a new {@code JndiPropertySource} with the default name
* {@value #JNDI_PROPERTY_SOURCE_NAME} and create a new {@link InitialContext}
* as the source object.
* @throws JndiLookupFailureException if a new {@link InitialContext}
* cannot be created.
* {@value #JNDI_PROPERTY_SOURCE_NAME} and a {@link JndiLocatorDelegate} configured
* to prefix any names with "java:comp/env/".
*/
public JndiPropertySource() throws JndiLookupFailureException {
this(JNDI_PROPERTY_SOURCE_NAME, createInitialContext(null));
public JndiPropertySource() {
this(JNDI_PROPERTY_SOURCE_NAME);
}
/**
* Create a new {@code JndiPropertySource} with the given name and
* create a new {@link InitialContext} as the source object.
* @throws JndiLookupFailureException if a new {@link InitialContext}
* cannot be created.
* Create a new {@code JndiPropertySource} with the given name
* and a {@link JndiLocatorDelegate} configured to prefix any names with
* "java:comp/env/".
*/
public JndiPropertySource(String name) throws JndiLookupFailureException {
this(name, createInitialContext(null));
public JndiPropertySource(String name) {
this(name, createDefaultJndiLocator());
}
/**
* Create a new {@code JndiPropertySource} with the given name and
* use the given jndiEnvironment properties to create a new
* {@link InitialContext} as the source object.
* @throws JndiLookupFailureException if a new {@link InitialContext}
* cannot be created.
* Create a new {@code JndiPropertySource} with the default name
* {@value #JNDI_PROPERTY_SOURCE_NAME} and the given {@code JndiLocatorDelegate}.
*/
public JndiPropertySource(String name, Properties jndiEnvironment) throws JndiLookupFailureException {
this(name, createInitialContext(jndiEnvironment));
public JndiPropertySource(JndiLocatorDelegate jndiLocator) {
this(JNDI_PROPERTY_SOURCE_NAME, jndiLocator);
}
/**
* Create a new {@code JndiPropertySource} with the given name and
* JNDI {@link Context}.
* Create a new {@code JndiPropertySource} with the given name and the given
* {@code JndiLocatorDelegate}.
*/
public JndiPropertySource(String name, Context source) {
super(name, source);
public JndiPropertySource(String name, JndiLocatorDelegate jndiLocator) {
super(name, jndiLocator);
}
/**
* {@inheritDoc}
* <p>This implementation looks up and returns the given name from the source JNDI
* {@link Context}. If a {@link NamingException} is thrown during the call to
* {@link Context#lookup(String)}, returns {@code null} and issue a DEBUG-level log
* statement with the exception message.
* <p>This implementation looks up and returns the value associated with the given
* name from the underlying {@link JndiLocatorDelegate}. If a {@link NamingException}
* is thrown during the call to {@link JndiLocatorDelegate#lookup(String)}, returns
* {@code null} and issues a DEBUG-level log statement with the exception message.
*/
@Override
public Object getProperty(String name) {
try {
Object value = this.source.lookup(name);
logger.debug("Context#lookup(" + name + ") returned: [" + value + "]");
logger.debug("JNDI lookup for name [" + name + "] returned: [" + value + "]");
return value;
} 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;
}
}
private static Context createInitialContext(Properties jndiEnvironment) {
try {
return new InitialContext(jndiEnvironment);
} catch (NamingException ex) {
throw new JndiLookupFailureException("unable to create InitialContext", ex);
}
/**
* Configure a {@code JndiLocatorDelegate} with its "resourceRef" property set to true
* meaning that all names will be prefixed with "java:comp/env/".
* @return
*/
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"));
}
}