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

Classes in this File | Line Coverage | Branch Coverage | Complexity
JNDIConfiguration |
95% | 102/107
100% | 18/18
| 2.8947368421052633;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 |  |
                     }
156 |  |
                     else
157 |  |
                     {
158 |  |
                         // add the key
159 | 80 |
                         keys.add(key.toString());
160 |  |
                     }
161 |  |
                 }
162 |  |
             }
163 |  |
             finally
164 |  |
             {
165 |  |
                 // close the enumeration
166 | 12 |
                 if (elements != null)
167 |  |
                 {
168 | 12 |
                     elements.close();
169 |  |
                 }
170 |  |
             }
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 |  |
                 }
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 |  |
                 }
268 |  |
             }
269 |  |
             finally
270 |  |
             {
271 | 10 |
                 if (elements != null)
272 |  |
                 {
273 | 10 |
                     elements.close();
274 |  |
                 }
275 |  |
             }
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 |  |
                 }
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 |  |
     }