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