##### Coverage Report - org.apache.commons.configuration.JNDIConfiguration

Classes in this File | Line Coverage | Branch Coverage | Complexity
JNDIConfiguration |
88% | 87/99
85% | 17/20
| 2.736842105263158;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 |  |
     }