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