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