blob: 7b2301b8843fadaa4640ee6beee325590c7c8e56 [file] [log] [blame]
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* 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.apache.commons.configuration;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
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.Log;
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 <a href="mailto:epugh@upstate.com">Eric Pugh</a>
* @version $Id: JNDIConfiguration.java,v 1.19 2004/10/04 20:06:09 ebourg Exp $
*/
public class JNDIConfiguration extends AbstractConfiguration
{
/** Logger. */
private static Log log = LogFactory.getLog(JNDIConfiguration.class);
/** The prefix of the context. */
private String prefix;
/** The initial JNDI context. */
private Context context;
/** The base JNDI context. */
private Context baseContext;
/** The Set of keys that have been virtually cleared. */
private Set clearedProperties = new HashSet();
/**
* 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.
*
* @param 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.
*
* @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.
*
* @param context the initial context
* @param prefix
*/
public JNDIConfiguration(Context context, String prefix)
{
this.context = context;
this.prefix = prefix;
}
/**
* JNDIConfigurations can not be added to.
*
* @param key The Key to add the property to.
* @param token The Value to add.
*/
public void addProperty(String key, Object token)
{
throw new UnsupportedOperationException("This operation is not supported");
}
/**
* 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.
* @throws NamingException If JNDI has an issue.
*/
private void recursiveGetKeys(Set keys, Context context, String prefix) throws NamingException
{
NamingEnumeration elements = null;
try
{
elements = context.list("");
// iterates through the context's elements
while (elements.hasMore())
{
NameClassPair nameClassPair = (NameClassPair) elements.next();
String name = nameClassPair.getName();
Object object = context.lookup(name);
// build the key
StringBuffer key = new StringBuffer();
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;
recursiveGetKeys(keys, subcontext, key.toString());
}
else
{
// add the key
keys.add(key.toString());
}
}
}
finally
{
// close the enumeration
if (elements != null)
{
elements.close();
}
}
}
/**
* {@inheritDoc}
*/
public Iterator getKeys()
{
return getKeys("");
}
/**
* {@inheritDoc}
*/
public Iterator getKeys(String prefix)
{
// build the path
String[] splitPath = StringUtils.split(prefix, ".");
List path = new ArrayList();
for (int i = 0; i < splitPath.length; i++)
{
path.add(splitPath[i]);
}
try
{
// find the context matching the specified path
Context context = getContext(path, getBaseContext());
// return all the keys under the context found
Set keys = new HashSet();
if (context != null)
{
recursiveGetKeys(keys, context, prefix);
}
else if (containsKey(prefix))
{
// add the prefix if it matches exactly a property key
keys.add(prefix);
}
return keys.iterator();
}
catch (NamingException e)
{
throw new ConfigurationRuntimeException(e.getMessage(), e);
}
}
/**
* 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 path, Context context) throws NamingException
{
// return the current context if the path is empty
if (path == null || path.isEmpty())
{
return context;
}
String key = (String) path.get(0);
// search a context matching the key in the context's elements
NamingEnumeration elements = null;
try
{
elements = context.list("");
while (elements.hasMore())
{
NameClassPair 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;
}
/**
* {@inheritDoc}
*
* <b>This operation is not supported</b>
*/
public Properties getProperties(String key)
{
throw new UnsupportedOperationException("This operation is not supported");
}
/**
* {@inheritDoc}
*/
public boolean isEmpty()
{
try
{
NamingEnumeration enumeration = null;
try
{
enumeration = getBaseContext().list("");
return !enumeration.hasMore();
}
finally
{
// close the enumeration
if (enumeration != null)
{
enumeration.close();
}
}
}
catch (NamingException ne)
{
log.warn(ne);
return true;
}
}
/**
* {@inheritDoc}
*/
public Object getProperty(String key)
{
return getPropertyDirect(key);
}
/**
* {@inheritDoc}
*/
public void setProperty(String key, Object value)
{
throw new UnsupportedOperationException("This operation is not supported");
}
/**
* {@inheritDoc}
*/
public void clearProperty(String key)
{
clearedProperties.add(key);
}
/**
* {@inheritDoc}
*/
public boolean containsKey(String key)
{
if (clearedProperties.contains(key))
{
return false;
}
key = StringUtils.replace(key, ".", "/");
try
{
// throws a NamingException if JNDI doesn't contain the key.
getBaseContext().lookup(key);
return true;
}
catch (NamingException ne)
{
return false;
}
}
/**
* @return String
*/
public String getPrefix()
{
return prefix;
}
/**
* Sets the prefix.
*
* @param prefix The prefix to set
*/
public void setPrefix(String prefix)
{
this.prefix = prefix;
// clear the previous baseContext
baseContext = null;
}
/**
* {@inheritDoc}
*/
protected Object getPropertyDirect(String key)
{
if (clearedProperties.contains(key))
{
return null;
}
try
{
key = StringUtils.replace(key, ".", "/");
return getBaseContext().lookup(key);
}
catch (NameNotFoundException e)
{
return null;
}
catch (NotContextException e)
{
return null;
}
catch (NamingException e)
{
e.printStackTrace();
return null;
}
}
/**
* {@inheritDoc}
*/
protected void addPropertyDirect(String key, Object obj)
{
throw new UnsupportedOperationException("This operation is not supported");
}
/**
* Return the base context with the prefix applied.
*/
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.
*/
public Context getContext()
{
return context;
}
/**
* Set the initial context of the configuration.
*/
public void setContext(Context context)
{
// forget the removed properties
clearedProperties.clear();
// change the context
this.context = context;
}
}