1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 497181 2007-01-17 21:35:28Z 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 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 this((String) null);
69 }
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 this(new InitialContext(), prefix);
82 }
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 this(context, null);
93 }
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 {
104 this.context = context;
105 this.prefix = prefix;
106 setLogger(LogFactory.getLog(getClass()));
107 addErrorLogListener();
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 fireError(EVENT_READ_PROPERTY, null, null, 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 fireError(EVENT_READ_PROPERTY, null, null, 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 fireError(EVENT_READ_PROPERTY, key, null, 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 (NotContextException nctxex)
406 {
407
408 return null;
409 }
410 catch (NamingException e)
411 {
412 fireError(EVENT_READ_PROPERTY, key, null, e);
413 return null;
414 }
415 }
416
417 /***
418 * <p><strong>This operation is not supported and will throw an
419 * UnsupportedOperationException.</strong></p>
420 *
421 * @param key the key
422 * @param obj the value
423 * @throws UnsupportedOperationException
424 */
425 protected void addPropertyDirect(String key, Object obj)
426 {
427 throw new UnsupportedOperationException("This operation is not supported");
428 }
429
430 /***
431 * Return the base context with the prefix applied.
432 *
433 * @return the base context
434 * @throws NamingException if an error occurs
435 */
436 public Context getBaseContext() throws NamingException
437 {
438 if (baseContext == null)
439 {
440 baseContext = (Context) getContext().lookup(prefix == null ? "" : prefix);
441 }
442
443 return baseContext;
444 }
445
446 /***
447 * Return the initial context used by this configuration. This context is
448 * independent of the prefix specified.
449 *
450 * @return the initial context
451 */
452 public Context getContext()
453 {
454 return context;
455 }
456
457 /***
458 * Set the initial context of the configuration.
459 *
460 * @param context the context
461 */
462 public void setContext(Context context)
463 {
464
465 clearedProperties.clear();
466
467
468 this.context = context;
469 }
470 }