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