Coverage Report - org.apache.commons.configuration.JNDIConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
JNDIConfiguration
88%
87/99
85%
17/20
2,737
 
 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  
 
 33  
 import org.apache.commons.lang.StringUtils;
 34  
 import org.apache.commons.logging.Log;
 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 439648 2006-09-02 20:42:10Z oheger $
 45  
  */
 46  10
 public class JNDIConfiguration extends AbstractConfiguration
 47  
 {
 48  
     /** Logger. */
 49  10
     private static Log log = LogFactory.getLog(JNDIConfiguration.class);
 50  
 
 51  
     /** The prefix of the context. */
 52  
     private String prefix;
 53  
 
 54  
     /** The initial JNDI context. */
 55  
     private Context context;
 56  
 
 57  
     /** The base JNDI context. */
 58  
     private Context baseContext;
 59  
 
 60  
     /** The Set of keys that have been virtually cleared. */
 61  49
     private Set clearedProperties = new HashSet();
 62  
 
 63  
     /**
 64  
      * Creates a JNDIConfiguration using the default initial context as the
 65  
      * root of the properties.
 66  
      *
 67  
      * @throws NamingException thrown if an error occurs when initializing the default context
 68  
      */
 69  
     public JNDIConfiguration() throws NamingException
 70  
     {
 71  47
         this((String) null);
 72  47
     }
 73  
 
 74  
     /**
 75  
      * Creates a JNDIConfiguration using the default initial context, shifted
 76  
      * with the specified prefix, as the root of the properties.
 77  
      *
 78  
      * @param prefix the prefix
 79  
      *
 80  
      * @throws NamingException thrown if an error occurs when initializing the default context
 81  
      */
 82  
     public JNDIConfiguration(String prefix) throws NamingException
 83  
     {
 84  47
         this(new InitialContext(), prefix);
 85  47
     }
 86  
 
 87  
     /**
 88  
      * Creates a JNDIConfiguration using the specified initial context as the
 89  
      * root of the properties.
 90  
      *
 91  
      * @param context the initial context
 92  
      */
 93  
     public JNDIConfiguration(Context context)
 94  
     {
 95  1
         this(context, null);
 96  1
     }
 97  
 
 98  
     /**
 99  
      * Creates a JNDIConfiguration using the specified initial context shifted
 100  
      * by the specified prefix as the root of the properties.
 101  
      *
 102  
      * @param context the initial context
 103  
      * @param prefix the prefix
 104  
      */
 105  
     public JNDIConfiguration(Context context, String prefix)
 106  49
     {
 107  49
         this.context = context;
 108  49
         this.prefix = prefix;
 109  49
     }
 110  
 
 111  
     /**
 112  
      * This method recursive traverse the JNDI tree, looking for Context objects.
 113  
      * When it finds them, it traverses them as well.  Otherwise it just adds the
 114  
      * values to the list of keys found.
 115  
      *
 116  
      * @param keys All the keys that have been found.
 117  
      * @param context The parent context
 118  
      * @param prefix What prefix we are building on.
 119  
      * @throws NamingException If JNDI has an issue.
 120  
      */
 121  
     private void recursiveGetKeys(Set keys, Context context, String prefix) throws NamingException
 122  
     {
 123  11
         NamingEnumeration elements = null;
 124  
 
 125  
         try
 126  
         {
 127  11
             elements = context.list("");
 128  
 
 129  
             // iterates through the context's elements
 130  96
             while (elements.hasMore())
 131  
             {
 132  74
                 NameClassPair nameClassPair = (NameClassPair) elements.next();
 133  74
                 String name = nameClassPair.getName();
 134  74
                 Object object = context.lookup(name);
 135  
 
 136  
                 // build the key
 137  74
                 StringBuffer key = new StringBuffer();
 138  74
                 key.append(prefix);
 139  74
                 if (key.length() > 0)
 140  
                 {
 141  70
                     key.append(".");
 142  
                 }
 143  74
                 key.append(name);
 144  
 
 145  74
                 if (object instanceof Context)
 146  
                 {
 147  
                     // add the keys of the sub context
 148  4
                     Context subcontext = (Context) object;
 149  4
                     recursiveGetKeys(keys, subcontext, key.toString());
 150  
                 }
 151  
                 else
 152  
                 {
 153  
                     // add the key
 154  70
                     keys.add(key.toString());
 155  
                 }
 156  
             }
 157  11
         }
 158  
         finally
 159  
         {
 160  
             // close the enumeration
 161  0
             if (elements != null)
 162  
             {
 163  11
                 elements.close();
 164  
             }
 165  
         }
 166  11
     }
 167  
 
 168  
     /**
 169  
      * Returns an iterator with all property keys stored in this configuration.
 170  
      *
 171  
      * @return an iterator with all keys
 172  
      */
 173  
     public Iterator getKeys()
 174  
     {
 175  4
         return getKeys("");
 176  
     }
 177  
 
 178  
     /**
 179  
      * Returns an iterator with all property keys starting with the given
 180  
      * prefix.
 181  
      *
 182  
      * @param prefix the prefix
 183  
      * @return an iterator with the selected keys
 184  
      */
 185  
     public Iterator getKeys(String prefix)
 186  
     {
 187  
         // build the path
 188  11
         String[] splitPath = StringUtils.split(prefix, ".");
 189  
 
 190  11
         List path = new ArrayList();
 191  
 
 192  22
         for (int i = 0; i < splitPath.length; i++)
 193  
         {
 194  11
             path.add(splitPath[i]);
 195  
         }
 196  
 
 197  
         try
 198  
         {
 199  
             // find the context matching the specified path
 200  11
             Context context = getContext(path, getBaseContext());
 201  
 
 202  
             // return all the keys under the context found
 203  11
             Set keys = new HashSet();
 204  11
             if (context != null)
 205  
             {
 206  7
                 recursiveGetKeys(keys, context, prefix);
 207  
             }
 208  4
             else if (containsKey(prefix))
 209  
             {
 210  
                 // add the prefix if it matches exactly a property key
 211  2
                 keys.add(prefix);
 212  
             }
 213  
 
 214  11
             return keys.iterator();
 215  
         }
 216  
         catch (NamingException e)
 217  
         {
 218  0
             log.error(e.getMessage(), e);
 219  0
             return new ArrayList().iterator();
 220  
         }
 221  
     }
 222  
 
 223  
     /**
 224  
      * Because JNDI is based on a tree configuration, we need to filter down the
 225  
      * tree, till we find the Context specified by the key to start from.
 226  
      * Otherwise return null.
 227  
      *
 228  
      * @param path     the path of keys to traverse in order to find the context
 229  
      * @param context  the context to start from
 230  
      * @return The context at that key's location in the JNDI tree, or null if not found
 231  
      * @throws NamingException if JNDI has an issue
 232  
      */
 233  
     private Context getContext(List path, Context context) throws NamingException
 234  
     {
 235  
         // return the current context if the path is empty
 236  16
         if (path == null || path.isEmpty())
 237  
         {
 238  7
             return context;
 239  
         }
 240  
 
 241  9
         String key = (String) path.get(0);
 242  
 
 243  
         // search a context matching the key in the context's elements
 244  9
         NamingEnumeration elements = null;
 245  
 
 246  
         try
 247  
         {
 248  9
             elements = context.list("");
 249  40
             while (elements.hasMore())
 250  
             {
 251  27
                 NameClassPair nameClassPair = (NameClassPair) elements.next();
 252  27
                 String name = nameClassPair.getName();
 253  27
                 Object object = context.lookup(name);
 254  
 
 255  27
                 if (object instanceof Context && name.equals(key))
 256  
                 {
 257  5
                     Context subcontext = (Context) object;
 258  
 
 259  
                     // recursive search in the sub context
 260  5
                     return getContext(path.subList(1, path.size()), subcontext);
 261  
                 }
 262  
             }
 263  4
         }
 264  
         finally
 265  
         {
 266  0
             if (elements != null)
 267  
             {
 268  9
                 elements.close();
 269  
             }
 270  
         }
 271  
 
 272  4
         return null;
 273  
     }
 274  
 
 275  
     /**
 276  
      * Returns a flag whether this configuration is empty.
 277  
      *
 278  
      * @return the empty flag
 279  
      */
 280  
     public boolean isEmpty()
 281  
     {
 282  
         try
 283  
         {
 284  2
             NamingEnumeration enumeration = null;
 285  
 
 286  
             try
 287  
             {
 288  2
                 enumeration = getBaseContext().list("");
 289  2
                 return !enumeration.hasMore();
 290  
             }
 291  
             finally
 292  
             {
 293  
                 // close the enumeration
 294  0
                 if (enumeration != null)
 295  
                 {
 296  2
                     enumeration.close();
 297  
                 }
 298  
             }
 299  
         }
 300  
         catch (NamingException e)
 301  
         {
 302  0
             log.error(e.getMessage(), e);
 303  0
             return true;
 304  
         }
 305  
     }
 306  
 
 307  
     /**
 308  
      * <p><strong>This operation is not supported and will throw an
 309  
      * UnsupportedOperationException.</strong></p>
 310  
      *
 311  
      * @param key the key
 312  
      * @param value the value
 313  
      * @throws UnsupportedOperationException
 314  
      */
 315  
     public void setProperty(String key, Object value)
 316  
     {
 317  0
         throw new UnsupportedOperationException("This operation is not supported");
 318  
     }
 319  
 
 320  
     /**
 321  
      * Removes the specified property.
 322  
      *
 323  
      * @param key the key of the property to remove
 324  
      */
 325  
     public void clearProperty(String key)
 326  
     {
 327  4
         clearedProperties.add(key);
 328  4
     }
 329  
 
 330  
     /**
 331  
      * Checks whether the specified key is contained in this configuration.
 332  
      *
 333  
      * @param key the key to check
 334  
      * @return a flag whether this key is stored in this configuration
 335  
      */
 336  
     public boolean containsKey(String key)
 337  
     {
 338  10
         if (clearedProperties.contains(key))
 339  
         {
 340  1
             return false;
 341  
         }
 342  9
         key = StringUtils.replace(key, ".", "/");
 343  
         try
 344  
         {
 345  
             // throws a NamingException if JNDI doesn't contain the key.
 346  9
             getBaseContext().lookup(key);
 347  5
             return true;
 348  
         }
 349  
         catch (NameNotFoundException e)
 350  
         {
 351  
             // expected exception, no need to log it
 352  4
             return false;
 353  
         }
 354  
         catch (NamingException e)
 355  
         {
 356  0
             log.error(e.getMessage(), e);
 357  0
             return false;
 358  
         }
 359  
     }
 360  
 
 361  
     /**
 362  
      * Returns the prefix.
 363  
      * @return the prefix
 364  
      */
 365  
     public String getPrefix()
 366  
     {
 367  0
         return prefix;
 368  
     }
 369  
 
 370  
     /**
 371  
      * Sets the prefix.
 372  
      *
 373  
      * @param prefix The prefix to set
 374  
      */
 375  
     public void setPrefix(String prefix)
 376  
     {
 377  3
         this.prefix = prefix;
 378  
 
 379  
         // clear the previous baseContext
 380  3
         baseContext = null;
 381  3
     }
 382  
 
 383  
     /**
 384  
      * Returns the value of the specified property.
 385  
      *
 386  
      * @param key the key of the property
 387  
      * @return the value of this property
 388  
      */
 389  
     public Object getProperty(String key)
 390  
     {
 391  69
         if (clearedProperties.contains(key))
 392  
         {
 393  3
             return null;
 394  
         }
 395  
 
 396  
         try
 397  
         {
 398  66
             key = StringUtils.replace(key, ".", "/");
 399  66
             return getBaseContext().lookup(key);
 400  
         }
 401  
         catch (NameNotFoundException e)
 402  
         {
 403  
             // expected exception, no need to log it
 404  7
             return null;
 405  
         }
 406  
         catch (NamingException e)
 407  
         {
 408  7
             log.error(e.getMessage(), e);
 409  7
             return null;
 410  
         }
 411  
     }
 412  
 
 413  
     /**
 414  
      * <p><strong>This operation is not supported and will throw an
 415  
      * UnsupportedOperationException.</strong></p>
 416  
      *
 417  
      * @param key the key
 418  
      * @param obj the value
 419  
      * @throws UnsupportedOperationException
 420  
      */
 421  
     protected void addPropertyDirect(String key, Object obj)
 422  
     {
 423  0
         throw new UnsupportedOperationException("This operation is not supported");
 424  
     }
 425  
 
 426  
     /**
 427  
      * Return the base context with the prefix applied.
 428  
      *
 429  
      * @return the base context
 430  
      * @throws NamingException if an error occurs
 431  
      */
 432  
     public Context getBaseContext() throws NamingException
 433  
     {
 434  88
         if (baseContext == null)
 435  
         {
 436  47
             baseContext = (Context) getContext().lookup(prefix == null ? "" : prefix);
 437  
         }
 438  
 
 439  88
         return baseContext;
 440  
     }
 441  
 
 442  
     /**
 443  
      * Return the initial context used by this configuration. This context is
 444  
      * independent of the prefix specified.
 445  
      *
 446  
      * @return the initial context
 447  
      */
 448  
     public Context getContext()
 449  
     {
 450  47
         return context;
 451  
     }
 452  
 
 453  
     /**
 454  
      * Set the initial context of the configuration.
 455  
      *
 456  
      * @param context the context
 457  
      */
 458  
     public void setContext(Context context)
 459  
     {
 460  
         // forget the removed properties
 461  1
         clearedProperties.clear();
 462  
 
 463  
         // change the context
 464  1
         this.context = context;
 465  1
     }
 466  
 }