/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.commons.configuration;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.NotContextException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;

This Configuration class allows you to interface with a JNDI datasource. A JNDIConfiguration is read-only, write operations will throw an UnsupportedOperationException. The clear operations are supported but the underlying JNDI data source is not changed.
Author:Eric Pugh
Version:$Id: JNDIConfiguration.java 1234985 2012-01-23 21:09:09Z oheger $
/** * This Configuration class allows you to interface with a JNDI datasource. * A JNDIConfiguration is read-only, write operations will throw an * UnsupportedOperationException. The clear operations are supported but the * underlying JNDI data source is not changed. * * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a> * @version $Id: JNDIConfiguration.java 1234985 2012-01-23 21:09:09Z oheger $ */
public class JNDIConfiguration extends AbstractConfiguration {
The prefix of the context.
/** The prefix of the context. */
private String prefix;
The initial JNDI context.
/** The initial JNDI context. */
private Context context;
The base JNDI context.
/** The base JNDI context. */
private Context baseContext;
The Set of keys that have been virtually cleared.
/** The Set of keys that have been virtually cleared. */
private Set<String> clearedProperties = new HashSet<String>();
Creates a JNDIConfiguration using the default initial context as the root of the properties.
Throws:
  • NamingException – thrown if an error occurs when initializing the default context
/** * Creates a JNDIConfiguration using the default initial context as the * root of the properties. * * @throws NamingException thrown if an error occurs when initializing the default context */
public JNDIConfiguration() throws NamingException { this((String) null); }
Creates a JNDIConfiguration using the default initial context, shifted with the specified prefix, as the root of the properties.
Params:
  • prefix – the prefix
Throws:
  • NamingException – thrown if an error occurs when initializing the default context
/** * Creates a JNDIConfiguration using the default initial context, shifted * with the specified prefix, as the root of the properties. * * @param prefix the prefix * * @throws NamingException thrown if an error occurs when initializing the default context */
public JNDIConfiguration(String prefix) throws NamingException { this(new InitialContext(), prefix); }
Creates a JNDIConfiguration using the specified initial context as the root of the properties.
Params:
  • context – the initial context
/** * Creates a JNDIConfiguration using the specified initial context as the * root of the properties. * * @param context the initial context */
public JNDIConfiguration(Context context) { this(context, null); }
Creates a JNDIConfiguration using the specified initial context shifted by the specified prefix as the root of the properties.
Params:
  • context – the initial context
  • prefix – the prefix
/** * Creates a JNDIConfiguration using the specified initial context shifted * by the specified prefix as the root of the properties. * * @param context the initial context * @param prefix the prefix */
public JNDIConfiguration(Context context, String prefix) { this.context = context; this.prefix = prefix; setLogger(LogFactory.getLog(getClass())); addErrorLogListener(); }
This method recursive traverse the JNDI tree, looking for Context objects. When it finds them, it traverses them as well. Otherwise it just adds the values to the list of keys found.
Params:
  • keys – All the keys that have been found.
  • context – The parent context
  • prefix – What prefix we are building on.
  • processedCtx – a set with the so far processed objects
Throws:
/** * This method recursive traverse the JNDI tree, looking for Context objects. * When it finds them, it traverses them as well. Otherwise it just adds the * values to the list of keys found. * * @param keys All the keys that have been found. * @param context The parent context * @param prefix What prefix we are building on. * @param processedCtx a set with the so far processed objects * @throws NamingException If JNDI has an issue. */
private void recursiveGetKeys(Set<String> keys, Context context, String prefix, Set<Context> processedCtx) throws NamingException { processedCtx.add(context); NamingEnumeration<NameClassPair> elements = null; try { elements = context.list(""); // iterates through the context's elements while (elements.hasMore()) { NameClassPair nameClassPair = elements.next(); String name = nameClassPair.getName(); Object object = context.lookup(name); // build the key StringBuilder key = new StringBuilder(); key.append(prefix); if (key.length() > 0) { key.append("."); } key.append(name); if (object instanceof Context) { // add the keys of the sub context Context subcontext = (Context) object; if (!processedCtx.contains(subcontext)) { recursiveGetKeys(keys, subcontext, key.toString(), processedCtx); } } else { // add the key keys.add(key.toString()); } } } finally { // close the enumeration if (elements != null) { elements.close(); } } }
Returns an iterator with all property keys stored in this configuration.
Returns:an iterator with all keys
/** * Returns an iterator with all property keys stored in this configuration. * * @return an iterator with all keys */
public Iterator<String> getKeys() { return getKeys(""); }
Returns an iterator with all property keys starting with the given prefix.
Params:
  • prefix – the prefix
Returns:an iterator with the selected keys
/** * Returns an iterator with all property keys starting with the given * prefix. * * @param prefix the prefix * @return an iterator with the selected keys */
@Override public Iterator<String> getKeys(String prefix) { // build the path String[] splitPath = StringUtils.split(prefix, "."); List<String> path = Arrays.asList(splitPath); try { // find the context matching the specified path Context context = getContext(path, getBaseContext()); // return all the keys under the context found Set<String> keys = new HashSet<String>(); if (context != null) { recursiveGetKeys(keys, context, prefix, new HashSet<Context>()); } else if (containsKey(prefix)) { // add the prefix if it matches exactly a property key keys.add(prefix); } return keys.iterator(); } catch (NameNotFoundException e) { // expected exception, no need to log it return new ArrayList<String>().iterator(); } catch (NamingException e) { fireError(EVENT_READ_PROPERTY, null, null, e); return new ArrayList<String>().iterator(); } }
Because JNDI is based on a tree configuration, we need to filter down the tree, till we find the Context specified by the key to start from. Otherwise return null.
Params:
  • path – the path of keys to traverse in order to find the context
  • context – the context to start from
Throws:
Returns:The context at that key's location in the JNDI tree, or null if not found
/** * Because JNDI is based on a tree configuration, we need to filter down the * tree, till we find the Context specified by the key to start from. * Otherwise return null. * * @param path the path of keys to traverse in order to find the context * @param context the context to start from * @return The context at that key's location in the JNDI tree, or null if not found * @throws NamingException if JNDI has an issue */
private Context getContext(List<String> path, Context context) throws NamingException { // return the current context if the path is empty if (path == null || path.isEmpty()) { return context; } String key = path.get(0); // search a context matching the key in the context's elements NamingEnumeration<NameClassPair> elements = null; try { elements = context.list(""); while (elements.hasMore()) { NameClassPair nameClassPair = elements.next(); String name = nameClassPair.getName(); Object object = context.lookup(name); if (object instanceof Context && name.equals(key)) { Context subcontext = (Context) object; // recursive search in the sub context return getContext(path.subList(1, path.size()), subcontext); } } } finally { if (elements != null) { elements.close(); } } return null; }
Returns a flag whether this configuration is empty.
Returns:the empty flag
/** * Returns a flag whether this configuration is empty. * * @return the empty flag */
public boolean isEmpty() { try { NamingEnumeration<NameClassPair> enumeration = null; try { enumeration = getBaseContext().list(""); return !enumeration.hasMore(); } finally { // close the enumeration if (enumeration != null) { enumeration.close(); } } } catch (NamingException e) { fireError(EVENT_READ_PROPERTY, null, null, e); return true; } }

This operation is not supported and will throw an UnsupportedOperationException.

Params:
  • key – the key
  • value – the value
Throws:
/** * <p><strong>This operation is not supported and will throw an * UnsupportedOperationException.</strong></p> * * @param key the key * @param value the value * @throws UnsupportedOperationException */
@Override public void setProperty(String key, Object value) { throw new UnsupportedOperationException("This operation is not supported"); }
Removes the specified property.
Params:
  • key – the key of the property to remove
/** * Removes the specified property. * * @param key the key of the property to remove */
@Override public void clearProperty(String key) { clearedProperties.add(key); }
Checks whether the specified key is contained in this configuration.
Params:
  • key – the key to check
Returns:a flag whether this key is stored in this configuration
/** * Checks whether the specified key is contained in this configuration. * * @param key the key to check * @return a flag whether this key is stored in this configuration */
public boolean containsKey(String key) { if (clearedProperties.contains(key)) { return false; } key = key.replaceAll("\\.", "/"); try { // throws a NamingException if JNDI doesn't contain the key. getBaseContext().lookup(key); return true; } catch (NameNotFoundException e) { // expected exception, no need to log it return false; } catch (NamingException e) { fireError(EVENT_READ_PROPERTY, key, null, e); return false; } }
Returns the prefix.
Returns:the prefix
/** * Returns the prefix. * @return the prefix */
public String getPrefix() { return prefix; }
Sets the prefix.
Params:
  • prefix – The prefix to set
/** * Sets the prefix. * * @param prefix The prefix to set */
public void setPrefix(String prefix) { this.prefix = prefix; // clear the previous baseContext baseContext = null; }
Returns the value of the specified property.
Params:
  • key – the key of the property
Returns:the value of this property
/** * Returns the value of the specified property. * * @param key the key of the property * @return the value of this property */
public Object getProperty(String key) { if (clearedProperties.contains(key)) { return null; } try { key = key.replaceAll("\\.", "/"); return getBaseContext().lookup(key); } catch (NameNotFoundException e) { // expected exception, no need to log it return null; } catch (NotContextException nctxex) { // expected exception, no need to log it return null; } catch (NamingException e) { fireError(EVENT_READ_PROPERTY, key, null, e); return null; } }

This operation is not supported and will throw an UnsupportedOperationException.

Params:
  • key – the key
  • obj – the value
Throws:
/** * <p><strong>This operation is not supported and will throw an * UnsupportedOperationException.</strong></p> * * @param key the key * @param obj the value * @throws UnsupportedOperationException */
@Override protected void addPropertyDirect(String key, Object obj) { throw new UnsupportedOperationException("This operation is not supported"); }
Return the base context with the prefix applied.
Throws:
Returns:the base context
/** * Return the base context with the prefix applied. * * @return the base context * @throws NamingException if an error occurs */
public Context getBaseContext() throws NamingException { if (baseContext == null) { baseContext = (Context) getContext().lookup(prefix == null ? "" : prefix); } return baseContext; }
Return the initial context used by this configuration. This context is independent of the prefix specified.
Returns:the initial context
/** * Return the initial context used by this configuration. This context is * independent of the prefix specified. * * @return the initial context */
public Context getContext() { return context; }
Set the initial context of the configuration.
Params:
  • context – the context
/** * Set the initial context of the configuration. * * @param context the context */
public void setContext(Context context) { // forget the removed properties clearedProperties.clear(); // change the context this.context = context; } }