View Javadoc

    1   /*
    2    * Copyright 2001-2004 The Apache Software Foundation.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License")
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16
    17  package org.apache.commons.configuration;
    18
    19  import java.util.ArrayList;
    20  import java.util.HashSet;
    21  import java.util.Iterator;
    22  import java.util.List;
    23  import java.util.Properties;
    24  import java.util.Set;
    25
    26  import javax.naming.Context;
    27  import javax.naming.InitialContext;
    28  import javax.naming.NameClassPair;
    29  import javax.naming.NameNotFoundException;
    30  import javax.naming.NamingEnumeration;
    31  import javax.naming.NamingException;
    32  import javax.naming.NotContextException;
    33
    34  import org.apache.commons.lang.StringUtils;
    35  import org.apache.commons.logging.Log;
    36  import org.apache.commons.logging.LogFactory;
    37
    38  /***
    39   * This Configuration class allows you to interface with a JNDI datasource.
    40   * A JNDIConfiguration is read-only, write operations will throw an
    41   * UnsupportedOperationException. The clear operations are supported but the
    42   * underlying JNDI data source is not changed.
    43   *
    44   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    45   * @version $Id: JNDIConfiguration.java,v 1.19 2004/10/04 20:06:09 ebourg Exp $
    46   */
    47  public class JNDIConfiguration extends AbstractConfiguration
    48  {
    49      /*** Logger. */
    50      private static Log log = LogFactory.getLog(JNDIConfiguration.class);
    51
    52      /*** The prefix of the context. */
    53      private String prefix;
    54
    55      /*** The initial JNDI context. */
    56      private Context context;
    57
    58      /*** The base JNDI context. */
    59      private Context baseContext;
    60
    61      /*** The Set of keys that have been virtually cleared. */
    62      private Set clearedProperties = new HashSet();
    63
    64      /***
    65       * Creates a JNDIConfiguration using the default initial context as the
    66       * root of the properties.
    67       *
    68       * @throws NamingException thrown if an error occurs when initializing the default context
    69       */
    70      public JNDIConfiguration() throws NamingException
    71      {
    72          this((String) null);
    73      }
    74
    75      /***
    76       * Creates a JNDIConfiguration using the default initial context, shifted
    77       * with the specified prefix, as the root of the properties.
    78       *
    79       * @param prefix
    80       *
    81       * @throws NamingException thrown if an error occurs when initializing the default context
    82       */
    83      public JNDIConfiguration(String prefix) throws NamingException
    84      {
    85          this(new InitialContext(), prefix);
    86      }
    87
    88      /***
    89       * Creates a JNDIConfiguration using the specified initial context as the
    90       * root of the properties.
    91       *
    92       * @param context the initial context
    93       */
    94      public JNDIConfiguration(Context context)
    95      {
    96          this(context, null);
    97      }
    98
    99      /***
    100      * Creates a JNDIConfiguration using the specified initial context shifted
    101      * by the specified prefix as the root of the properties.
    102      *
    103      * @param context the initial context
    104      * @param prefix
    105      */
    106     public JNDIConfiguration(Context context, String prefix)
    107     {
    108         this.context = context;
    109         this.prefix = prefix;
    110     }
    111
    112     /***
    113      * JNDIConfigurations can not be added to.
    114      *
    115      * @param key The Key to add the property to.
    116      * @param token The Value to add.
    117      */
    118     public void addProperty(String key, Object token)
    119     {
    120         throw new UnsupportedOperationException("This operation is not supported");
    121     }
    122
    123     /***
    124      * This method recursive traverse the JNDI tree, looking for Context objects.
    125      * When it finds them, it traverses them as well.  Otherwise it just adds the
    126      * values to the list of keys found.
    127      *
    128      * @param keys All the keys that have been found.
    129      * @param context The parent context
    130      * @param prefix What prefix we are building on.
    131      * @throws NamingException If JNDI has an issue.
    132      */
    133     private void recursiveGetKeys(Set keys, Context context, String prefix) throws NamingException
    134     {
    135         NamingEnumeration elements = null;
    136
    137         try
    138         {
    139             elements = context.list("");
    140
    141             // iterates through the context's elements
    142             while (elements.hasMore())
    143             {
    144                 NameClassPair nameClassPair = (NameClassPair) elements.next();
    145                 String name = nameClassPair.getName();
    146                 Object object = context.lookup(name);
    147
    148                 // build the key
    149                 StringBuffer key = new StringBuffer();
    150                 key.append(prefix);
    151                 if (key.length() > 0)
    152                 {
    153                     key.append(".");
    154                 }
    155                 key.append(name);
    156
    157                 if (object instanceof Context)
    158                 {
    159                     // add the keys of the sub context
    160                     Context subcontext = (Context) object;
    161                     recursiveGetKeys(keys, subcontext, key.toString());
    162                 }
    163                 else
    164                 {
    165                     // add the key
    166                     keys.add(key.toString());
    167                 }
    168             }
    169         }
    170         finally
    171         {
    172             // close the enumeration
    173             if (elements != null)
    174             {
    175                 elements.close();
    176             }
    177         }
    178     }
    179
    180     /***
    181      * {@inheritDoc}
    182      */
    183     public Iterator getKeys()
    184     {
    185         return getKeys("");
    186     }
    187
    188     /***
    189      * {@inheritDoc}
    190      */
    191     public Iterator getKeys(String prefix)
    192     {
    193         // build the path
    194         String[] splitPath = StringUtils.split(prefix, ".");
    195
    196         List path = new ArrayList();
    197
    198         for (int i = 0; i < splitPath.length; i++)
    199         {
    200             path.add(splitPath[i]);
    201         }
    202
    203         try
    204         {
    205             // find the context matching the specified path
    206             Context context = getContext(path, getBaseContext());
    207
    208             // return all the keys under the context found
    209             Set keys = new HashSet();
    210             if (context != null)
    211             {
    212                 recursiveGetKeys(keys, context, prefix);
    213             }
    214             else if (containsKey(prefix))
    215             {
    216                 // add the prefix if it matches exactly a property key
    217                 keys.add(prefix);
    218             }
    219
    220             return keys.iterator();
    221         }
    222         catch (NamingException e)
    223         {
    224             throw new ConfigurationRuntimeException(e.getMessage(), e);
    225         }
    226     }
    227
    228     /***
    229      * Because JNDI is based on a tree configuration, we need to filter down the
    230      * tree, till we find the Context specified by the key to start from.
    231      * Otherwise return null.
    232      *
    233      * @param path     the path of keys to traverse in order to find the context
    234      * @param context  the context to start from
    235      * @return The context at that key's location in the JNDI tree, or null if not found
    236      * @throws NamingException if JNDI has an issue
    237      */
    238     private Context getContext(List path, Context context) throws NamingException
    239     {
    240         // return the current context if the path is empty
    241         if (path == null || path.isEmpty())
    242         {
    243             return context;
    244         }
    245
    246         String key = (String) path.get(0);
    247
    248         // search a context matching the key in the context's elements
    249         NamingEnumeration elements = null;
    250
    251         try
    252         {
    253             elements = context.list("");
    254             while (elements.hasMore())
    255             {
    256                 NameClassPair nameClassPair = (NameClassPair) elements.next();
    257                 String name = nameClassPair.getName();
    258                 Object object = context.lookup(name);
    259
    260                 if (object instanceof Context && name.equals(key))
    261                 {
    262                     Context subcontext = (Context) object;
    263
    264                     // recursive search in the sub context
    265                     return getContext(path.subList(1, path.size()), subcontext);
    266                 }
    267             }
    268         }
    269         finally
    270         {
    271             if (elements != null)
    272             {
    273                 elements.close();
    274             }
    275         }
    276
    277         return null;
    278     }
    279
    280     /***
    281      * {@inheritDoc}
    282      *
    283      * <b>This operation is not supported</b>
    284      */
    285     public Properties getProperties(String key)
    286     {
    287         throw new UnsupportedOperationException("This operation is not supported");
    288     }
    289
    290     /***
    291      * {@inheritDoc}
    292      */
    293     public boolean isEmpty()
    294     {
    295         try
    296         {
    297             NamingEnumeration enumeration = null;
    298
    299             try
    300             {
    301                 enumeration = getBaseContext().list("");
    302                 return !enumeration.hasMore();
    303             }
    304             finally
    305             {
    306                 // close the enumeration
    307                 if (enumeration != null)
    308                 {
    309                     enumeration.close();
    310                 }
    311             }
    312         }
    313         catch (NamingException ne)
    314         {
    315             log.warn(ne);
    316             return true;
    317         }
    318     }
    319
    320     /***
    321      * {@inheritDoc}
    322      */
    323     public Object getProperty(String key)
    324     {
    325         return getPropertyDirect(key);
    326     }
    327
    328     /***
    329      * {@inheritDoc}
    330      */
    331     public void setProperty(String key, Object value)
    332     {
    333         throw new UnsupportedOperationException("This operation is not supported");
    334     }
    335
    336     /***
    337      * {@inheritDoc}
    338      */
    339     public void clearProperty(String key)
    340     {
    341         clearedProperties.add(key);
    342     }
    343
    344     /***
    345      * {@inheritDoc}
    346      */
    347     public boolean containsKey(String key)
    348     {
    349         if (clearedProperties.contains(key))
    350         {
    351             return false;
    352         }
    353         key = StringUtils.replace(key, ".", "/");
    354         try
    355         {
    356             // throws a NamingException if JNDI doesn't contain the key.
    357             getBaseContext().lookup(key);
    358             return true;
    359         }
    360         catch (NamingException ne)
    361         {
    362             return false;
    363         }
    364     }
    365
    366     /***
    367      * @return String
    368      */
    369     public String getPrefix()
    370     {
    371         return prefix;
    372     }
    373
    374     /***
    375      * Sets the prefix.
    376      *
    377      * @param prefix The prefix to set
    378      */
    379     public void setPrefix(String prefix)
    380     {
    381         this.prefix = prefix;
    382
    383         // clear the previous baseContext
    384         baseContext = null;
    385     }
    386
    387     /***
    388      * {@inheritDoc}
    389      */
    390     protected Object getPropertyDirect(String key)
    391     {
    392         if (clearedProperties.contains(key))
    393         {
    394             return null;
    395         }
    396
    397         try
    398         {
    399             key = StringUtils.replace(key, ".", "/");
    400             return getBaseContext().lookup(key);
    401         }
    402         catch (NameNotFoundException e)
    403         {
    404             return null;
    405         }
    406         catch (NotContextException e)
    407         {
    408             return null;
    409         }
    410         catch (NamingException e)
    411         {
    412             e.printStackTrace();
    413             return null;
    414         }
    415     }
    416
    417     /***
    418      * {@inheritDoc}
    419      */
    420     protected void addPropertyDirect(String key, Object obj)
    421     {
    422         throw new UnsupportedOperationException("This operation is not supported");
    423     }
    424
    425     /***
    426      * Return the base context with the prefix applied.
    427      */
    428     public Context getBaseContext() throws NamingException
    429     {
    430         if (baseContext == null)
    431         {
    432             baseContext = (Context) getContext().lookup(prefix == null ? "" : prefix);
    433         }
    434
    435         return baseContext;
    436     }
    437
    438     /***
    439      * Return the initial context used by this configuration. This context is
    440      * independent of the prefix specified.
    441      */
    442     public Context getContext()
    443     {
    444         return context;
    445     }
    446
    447     /***
    448      * Set the initial context of the configuration.
    449      */
    450     public void setContext(Context context)
    451     {
    452         // forget the removed properties
    453         clearedProperties.clear();
    454
    455         // change the context
    456         this.context = context;
    457     }
    458 }