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
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 public class JNDIConfiguration extends AbstractConfiguration
47 {
48 /*** Logger. */
49 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 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 this((String) null);
72 }
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 this(new InitialContext(), prefix);
85 }
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 this(context, null);
96 }
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 {
107 this.context = context;
108 this.prefix = prefix;
109 }
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 NamingEnumeration elements = null;
124
125 try
126 {
127 elements = context.list("");
128
129
130 while (elements.hasMore())
131 {
132 NameClassPair nameClassPair = (NameClassPair) elements.next();
133 String name = nameClassPair.getName();
134 Object object = context.lookup(name);
135
136
137 StringBuffer key = new StringBuffer();
138 key.append(prefix);
139 if (key.length() > 0)
140 {
141 key.append(".");
142 }
143 key.append(name);
144
145 if (object instanceof Context)
146 {
147
148 Context subcontext = (Context) object;
149 recursiveGetKeys(keys, subcontext, key.toString());
150 }
151 else
152 {
153
154 keys.add(key.toString());
155 }
156 }
157 }
158 finally
159 {
160
161 if (elements != null)
162 {
163 elements.close();
164 }
165 }
166 }
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 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
188 String[] splitPath = StringUtils.split(prefix, ".");
189
190 List path = new ArrayList();
191
192 for (int i = 0; i < splitPath.length; i++)
193 {
194 path.add(splitPath[i]);
195 }
196
197 try
198 {
199
200 Context context = getContext(path, getBaseContext());
201
202
203 Set keys = new HashSet();
204 if (context != null)
205 {
206 recursiveGetKeys(keys, context, prefix);
207 }
208 else if (containsKey(prefix))
209 {
210
211 keys.add(prefix);
212 }
213
214 return keys.iterator();
215 }
216 catch (NamingException e)
217 {
218 log.error(e.getMessage(), e);
219 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
236 if (path == null || path.isEmpty())
237 {
238 return context;
239 }
240
241 String key = (String) path.get(0);
242
243
244 NamingEnumeration elements = null;
245
246 try
247 {
248 elements = context.list("");
249 while (elements.hasMore())
250 {
251 NameClassPair nameClassPair = (NameClassPair) elements.next();
252 String name = nameClassPair.getName();
253 Object object = context.lookup(name);
254
255 if (object instanceof Context && name.equals(key))
256 {
257 Context subcontext = (Context) object;
258
259
260 return getContext(path.subList(1, path.size()), subcontext);
261 }
262 }
263 }
264 finally
265 {
266 if (elements != null)
267 {
268 elements.close();
269 }
270 }
271
272 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 NamingEnumeration enumeration = null;
285
286 try
287 {
288 enumeration = getBaseContext().list("");
289 return !enumeration.hasMore();
290 }
291 finally
292 {
293
294 if (enumeration != null)
295 {
296 enumeration.close();
297 }
298 }
299 }
300 catch (NamingException e)
301 {
302 log.error(e.getMessage(), e);
303 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 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 clearedProperties.add(key);
328 }
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 if (clearedProperties.contains(key))
339 {
340 return false;
341 }
342 key = StringUtils.replace(key, ".", "/");
343 try
344 {
345
346 getBaseContext().lookup(key);
347 return true;
348 }
349 catch (NameNotFoundException e)
350 {
351
352 return false;
353 }
354 catch (NamingException e)
355 {
356 log.error(e.getMessage(), e);
357 return false;
358 }
359 }
360
361 /***
362 * Returns the prefix.
363 * @return the prefix
364 */
365 public String getPrefix()
366 {
367 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 this.prefix = prefix;
378
379
380 baseContext = null;
381 }
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 if (clearedProperties.contains(key))
392 {
393 return null;
394 }
395
396 try
397 {
398 key = StringUtils.replace(key, ".", "/");
399 return getBaseContext().lookup(key);
400 }
401 catch (NameNotFoundException e)
402 {
403
404 return null;
405 }
406 catch (NamingException e)
407 {
408 log.error(e.getMessage(), e);
409 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 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 if (baseContext == null)
435 {
436 baseContext = (Context) getContext().lookup(prefix == null ? "" : prefix);
437 }
438
439 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 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
461 clearedProperties.clear();
462
463
464 this.context = context;
465 }
466 }