001: package net.myvietnam.mvncore.configuration;
002:
003: /* ====================================================================
004: * The Apache Software License, Version 1.1
005: *
006: * Copyright (c) 1999-2003 The Apache Software Foundation. All rights
007: * reserved.
008: *
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowledgement:
023: * "This product includes software developed by the
024: * Apache Software Foundation (http://www.apache.org/)."
025: * Alternately, this acknowledgement may appear in the software itself,
026: * if and wherever such third-party acknowledgements normally appear.
027: *
028: * 4. The names "The Jakarta Project", "Commons", and "Apache Software
029: * Foundation" must not be used to endorse or promote products derived
030: * from this software without prior written permission. For written
031: * permission, please contact apache@apache.org.
032: *
033: * 5. Products derived from this software may not be called "Apache"
034: * nor may "Apache" appear in their names without prior written
035: * permission of the Apache Software Foundation.
036: *
037: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
038: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
039: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
040: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
041: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
042: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
043: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
044: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
045: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
046: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
047: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
048: * SUCH DAMAGE.
049: * ====================================================================
050: *
051: * This software consists of voluntary contributions made by many
052: * individuals on behalf of the Apache Software Foundation. For more
053: * information on the Apache Software Foundation, please see
054: * <http://www.apache.org/>.
055: */
056: import java.util.ArrayList;
057: import java.util.Iterator;
058: import java.util.List;
059: import java.util.NoSuchElementException;
060: import java.util.Properties;
061: import java.util.Vector;
062: import javax.naming.Binding;
063: import javax.naming.Context;
064: import javax.naming.InitialContext;
065: import javax.naming.NamingEnumeration;
066: import javax.naming.NamingException;
067: import org.apache.commons.lang.StringUtils;
068: import org.apache.commons.logging.Log;
069: import org.apache.commons.logging.LogFactory;
070:
071: /**
072: * This Configuration class allows you to interface with a JNDI datasource.
073: *
074: * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
075: * @version $Id: JNDIConfiguration.java,v 1.5 2007/10/16 06:54:48 lexuanttkhtn Exp $
076: */
077: public class JNDIConfiguration extends BaseConfiguration implements
078: Configuration {
079:
080: private static Log log = LogFactory.getLog(JNDIConfiguration.class);
081:
082: private String prefix;
083: private Context envCtx;
084: private List clearedProperties = new ArrayList();
085:
086: /**
087: * Creates an empty JNDIConfiguration object which can then
088: * be added some other Configuration files
089: */
090: public JNDIConfiguration() {
091: }
092:
093: /**
094: * JNDIConfigurations can not be added to
095: *
096: * @param key The Key to add the property to.
097: * @param token The Value to add.
098: */
099: public void addProperty(String key, Object token) {
100: throw new Error("This operation is not supported");
101: }
102:
103: /**
104: * This method recursive traverse the JNDI tree, looking for Context objects.
105: * When it finds them, it traverses them as well. Otherwise it just adds the
106: * values to the list of keys found.
107: * @param keys All the keys that have been found.
108: * @param enum An enumeration of all the elements found at a specific context
109: * @param key What key we are building on.
110: * @throws NamingException If JNDI has an issue.
111: */
112: private void recursiveGetKeys(List keys,
113: NamingEnumeration enumeration, String key)
114: throws NamingException {
115: while (enumeration.hasMoreElements()) {
116: Binding binding = (Binding) enumeration.next();
117: StringBuffer newKey = new StringBuffer();
118: newKey.append(key);
119: if (newKey.length() > 0) {
120: newKey.append(".");
121: }
122: newKey.append(binding.getName());
123: if (binding.getObject() instanceof Context) {
124: Context c = (Context) binding.getObject();
125: NamingEnumeration enum2 = c.listBindings("");
126: recursiveGetKeys(keys, enum2, newKey.toString());
127: } else {
128: if (!keys.contains(newKey.toString())) {
129: keys.add(newKey.toString());
130: }
131: }
132: }
133: }
134:
135: /**
136: * Get the list of the keys contained in the configuration
137: * repository.
138: *
139: * @return An Iterator.
140: */
141: public Iterator getKeys() {
142: return getKeys("");
143: }
144:
145: /**
146: * Get the list of the keys contained in the configuration
147: * repository that match a passed in beginning pattern.
148: *
149: * @param key the key pattern to match on.
150: * @return An Iterator.
151: */
152: public Iterator getKeys(String key) {
153: List keys = new ArrayList();
154: try {
155: String[] splitKeys = StringUtils.split(key, ".");
156: for (int i = 0; i < splitKeys.length; i++) {
157: keys.add(splitKeys[i]);
158: }
159: Context context = null;
160: if (keys.size() == 0) {
161: context = getContext();
162: } else {
163: context = getStartingContextPoint(keys, getContext()
164: .listBindings(""));
165: }
166: if (context != null) {
167: NamingEnumeration enumeration = context
168: .listBindings("");
169: recursiveGetKeys(keys, enumeration, key);
170: }
171: } catch (NamingException ne) {
172: log.warn(ne);
173: }
174: return keys.iterator();
175: }
176:
177: /**
178: * Because JNDI is based on a tree configuration, we need to filter down the
179: * tree, till we find the Context specified by the key to start from.
180: * Otherwise return null.
181: *
182: * @param The key (or name) of the Context we are looking to start from.
183: * @return The context at that key's location in the JNDI tree, or null if not found
184: * @throws NamingException if JNDI has an issue
185: */
186: private Context getStartingContextPoint(List keys,
187: NamingEnumeration enumeration) throws NamingException {
188: String keyToSearchFor = (String) keys.get(0);
189: log.debug("Key to search for is " + keyToSearchFor);
190: while (enumeration.hasMoreElements()) {
191: Binding binding = (Binding) enumeration.next();
192: log.debug("Binding for name: " + binding.getName()
193: + ", object:" + binding.getObject() + ", class:"
194: + binding.getClassName());
195: if (binding.getObject() instanceof Context
196: && binding.getName().equals(keyToSearchFor)) {
197: keys.remove(0);
198: Context c = (Context) binding.getObject();
199: if (keys.size() > 0) {
200: return getStartingContextPoint(keys, c
201: .listBindings(""));
202: } else {
203: return c;
204: }
205: }
206: }
207: return null;
208: }
209:
210: /**
211: * Get a list of properties associated with the given
212: * configuration key.
213: *
214: * @param key The configuration key.
215: * @return The associated properties if key is found.
216: * @throws ClassCastException is thrown if the key maps to an
217: * object that is not a String/Vector.
218: * @throws IllegalArgumentException if one of the tokens is
219: * malformed (does not contain an equals sign).
220: * @see #getProperties(String, Properties)
221: */
222: public Properties getProperties(String key) {
223: throw new Error("This operation is not supported");
224: }
225:
226: public boolean isEmpty() {
227: try {
228: NamingEnumeration enumeration = getContext().listBindings(
229: "");
230: return !enumeration.hasMore();
231: } catch (NamingException ne) {
232: log.warn(ne);
233: return true;
234: }
235: }
236:
237: /**
238: * Gets a property from the configuration.
239: *
240: * @param key property to retrieve
241: * @return value as object. Will return user value if exists,
242: * if not then default value if exists, otherwise null
243: */
244: public Object getProperty(String key) {
245: throw new Error("This operation is not supported");
246: }
247:
248: /**
249: * Set a property, this will replace any previously
250: * set values. Set values is implicitly a call
251: * to clearProperty(key), addProperty(key,value).
252: *
253: * @param key
254: * @param value
255: */
256: public void setProperty(String key, Object value) {
257: throw new Error("This operation is not supported");
258: }
259:
260: /**
261: * Clear a property in the configuration. Just marks it as cleared,
262: * doesn't change the underlying JNDI data source.
263: *
264: * @param key the key to remove along with corresponding value.
265: */
266: public void clearProperty(String key) {
267: if (!clearedProperties.contains(key)) {
268: clearedProperties.add(key);
269: }
270: }
271:
272: /**
273: * check if the configuration contains the key, or the key
274: * has been removed.
275: */
276: public boolean containsKey(String key) {
277: if (clearedProperties.contains(key)) {
278: return false;
279: }
280: key = StringUtils.replace(key, ".", "/");
281: try {
282: // throws a NamingException if JNDI doesn't contain the key.
283: getContext().lookup(key);
284: return true;
285: } catch (javax.naming.NamingException ne) {
286: return false;
287: }
288: }
289:
290: /**
291: * Create an ExtendedProperties object that is a subset
292: * of this one. Take into account duplicate keys
293: * by using the setProperty() in ExtendedProperties.
294: *
295: * @param prefix
296: */
297: public Configuration subset(String prefix) {
298: BaseConfiguration c = new BaseConfiguration();
299: Iterator keys = this .getKeys();
300: boolean validSubset = false;
301: while (keys.hasNext()) {
302: Object key = keys.next();
303: if (key instanceof String
304: && ((String) key).startsWith(prefix)) {
305: if (!validSubset) {
306: validSubset = true;
307: }
308: String newKey = null;
309: /*
310: * Check to make sure that c.subset(prefix) doesn't blow up when
311: * there is only a single property with the key prefix. This is
312: * not a useful subset but it is a valid subset.
313: */
314: if (((String) key).length() == prefix.length()) {
315: newKey = prefix;
316: } else {
317: newKey = ((String) key)
318: .substring(prefix.length() + 1);
319: }
320: /*
321: * use addPropertyDirect() - this will plug the data as is into
322: * the Map, but will also do the right thing re key accounting
323: */
324: Object value = getValueFromJNDI(key.toString());
325: if (value instanceof String) {
326: c.addPropertyDirect(newKey,
327: interpolate((String) value));
328: } else {
329: c.addPropertyDirect(newKey, value);
330: }
331: }
332: }
333: if (validSubset) {
334: return c;
335: } else {
336: return null;
337: }
338: }
339:
340: /**
341: * Get a boolean associated with the given configuration key.
342: *
343: * @param key The configuration key.
344: * @param defaultValue The default value.
345: * @return The associated boolean if key is found and has valid
346: * format, default value otherwise.
347: * @throws ClassCastException is thrown if the key maps to an
348: * object that is not a Boolean.
349: */
350: public Boolean getBoolean(String key, Boolean defaultValue) {
351: Object value = getValueFromJNDI(key);
352: if (value instanceof Boolean) {
353: return (Boolean) value;
354: } else if (value instanceof String) {
355: return testBoolean((String) value);
356: } else if (value == null) {
357: if (defaults != null) {
358: return defaults.getBoolean(key, defaultValue);
359: } else {
360: return defaultValue;
361: }
362: } else {
363: throw new ClassCastException('\'' + key
364: + "' doesn't map to a Boolean object");
365: }
366: }
367:
368: /**
369: * Get a byte associated with the given configuration key.
370: *
371: * @param key The configuration key.
372: * @param defaultValue The default value.
373: * @return The associated byte if key is found and has valid format, default
374: * value otherwise.
375: * @throws ClassCastException is thrown if the key maps to an object that
376: * is not a Byte.
377: * @throws NumberFormatException is thrown if the value mapped by the key
378: * has not a valid number format.
379: */
380: public Byte getByte(String key, Byte defaultValue) {
381: Object value = getValueFromJNDI(key);
382: if (value instanceof Byte) {
383: return (Byte) value;
384: } else if (value instanceof String) {
385: Byte b = new Byte((String) value);
386: return b;
387: } else if (value == null) {
388: return defaultValue;
389: } else {
390: throw new ClassCastException('\'' + key
391: + "' doesn't map to a Byte object");
392: }
393: }
394:
395: /**
396: * Get a double associated with the given configuration key.
397: *
398: * @param key The configuration key.
399: * @param defaultValue The default value.
400: * @return The associated double if key is found and has valid
401: * format, default value otherwise.
402: * @throws ClassCastException is thrown if the key maps to an
403: * object that is not a Double.
404: * @throws NumberFormatException is thrown if the value mapped
405: * by the key has not a valid number format.
406: */
407: public Double getDouble(String key, Double defaultValue) {
408: Object value = this .getValueFromJNDI(key);
409: if (value instanceof Double) {
410: return (Double) value;
411: } else if (value instanceof String) {
412: Double d = new Double((String) value);
413: return d;
414: } else if (value == null) {
415: return defaultValue;
416: } else {
417: throw new ClassCastException('\'' + key
418: + "' doesn't map to a Double object");
419: }
420: }
421:
422: /**
423: * Get a float associated with the given configuration key.
424: *
425: * @param key The configuration key.
426: * @param defaultValue The default value.
427: * @return The associated float if key is found and has valid
428: * format, default value otherwise.
429: * @throws ClassCastException is thrown if the key maps to an
430: * object that is not a Float.
431: * @throws NumberFormatException is thrown if the value mapped
432: * by the key has not a valid number format.
433: */
434: public Float getFloat(String key, Float defaultValue) {
435: Object value = getValueFromJNDI(key);
436: if (value instanceof Float) {
437: return (Float) value;
438: } else if (value instanceof String) {
439: Float f = new Float((String) value);
440: return f;
441: } else if (value == null) {
442: return defaultValue;
443: } else {
444: throw new ClassCastException('\'' + key
445: + "' doesn't map to a Float object");
446: }
447: }
448:
449: /**
450: * Get a int associated with the given configuration key.
451: *
452: * @param key The configuration key.
453: * @param defaultValue The default value.
454: * @return The associated int if key is found and has valid format, default
455: * value otherwise.
456: * @throws ClassCastException is thrown if the key maps to an object that
457: * is not a Integer.
458: * @throws NumberFormatException is thrown if the value mapped by the key
459: * has not a valid number format.
460: */
461: public Integer getInteger(String key, Integer defaultValue) {
462: Object value = getValueFromJNDI(key);
463: if (value instanceof Integer) {
464: return (Integer) value;
465: } else if (value instanceof String) {
466: Integer i = new Integer((String) value);
467: return i;
468: } else if (value == null) {
469: return defaultValue;
470: } else {
471: throw new ClassCastException('\'' + key
472: + "' doesn't map to a Integer object");
473: }
474: }
475:
476: /**
477: * Get a long associated with the given configuration key.
478: *
479: * @param key The configuration key.
480: * @param defaultValue The default value.
481: * @return The associated long if key is found and has valid
482: * format, default value otherwise.
483: * @throws ClassCastException is thrown if the key maps to an
484: * object that is not a Long.
485: * @throws NumberFormatException is thrown if the value mapped
486: * by the key has not a valid number format.
487: */
488: public Long getLong(String key, Long defaultValue) {
489: Object value = getValueFromJNDI(key);
490: if (value instanceof Long) {
491: return (Long) value;
492: } else if (value instanceof String) {
493: Long l = new Long((String) value);
494: return l;
495: } else if (value == null) {
496: return defaultValue;
497: } else {
498: throw new ClassCastException('\'' + key
499: + "' doesn't map to a Long object");
500: }
501: }
502:
503: /**
504: * Get a short associated with the given configuration key.
505: *
506: * @param key The configuration key.
507: * @param defaultValue The default value.
508: * @return The associated short if key is found and has valid
509: * format, default value otherwise.
510: * @throws ClassCastException is thrown if the key maps to an
511: * object that is not a Short.
512: * @throws NumberFormatException is thrown if the value mapped
513: * by the key has not a valid number format.
514: */
515: public Short getShort(String key, Short defaultValue) {
516: Object value = getValueFromJNDI(key);
517: if (value instanceof Short) {
518: return (Short) value;
519: } else if (value instanceof String) {
520: Short s = new Short((String) value);
521: return s;
522: } else if (value == null) {
523: return defaultValue;
524: } else {
525: throw new ClassCastException('\'' + key
526: + "' doesn't map to a Short object");
527: }
528: }
529:
530: /**
531: * Get a string associated with the given configuration key.
532: *
533: * @param key The configuration key.
534: * @param defaultValue The default value.
535: * @return The associated string if key is found, default value otherwise.
536: * @throws ClassCastException is thrown if the key maps to an object that
537: * is not a String.
538: */
539: public String getString(String key, String defaultValue) {
540: try {
541: Object o = getValueFromJNDI(key);
542: if (o == null) {
543: return defaultValue;
544: } else {
545: return (String) o;
546: }
547: } catch (NoSuchElementException nsee) {
548: return defaultValue;
549: }
550: }
551:
552: /**
553: * Get an array of strings associated with the given configuration
554: * key.
555: *
556: * @param key The configuration key.
557: * @return The associated string array if key is found.
558: * @throws ClassCastException is thrown if the key maps to an
559: * object that is not a String/Vector of Strings.
560: */
561: public String[] getStringArray(String key) {
562: Object value = getValueFromJNDI(key);
563: String[] tokens;
564: if (value instanceof String) {
565: tokens = new String[1];
566: tokens[0] = interpolate((String) value);
567: } else if (value instanceof Container) {
568: tokens = new String[((Container) value).size()];
569: for (int i = 0; i < tokens.length; i++) {
570: tokens[i] = interpolate((String) ((Container) value)
571: .get(i));
572: }
573: } else if (value == null) {
574: tokens = new String[0];
575: } else {
576: throw new ClassCastException('\'' + key
577: + "' doesn't map to a String/Vector object");
578: }
579: return tokens;
580: }
581:
582: /**
583: * Get a Vector of strings associated with the given configuration key.
584: * Typically this will be just a single item, as you can't have multiple
585: * properties with the same name.
586: *
587: * @param key The configuration key.
588: * @param defaultValue The default value.
589: * @return The associated Vector.
590: */
591: public Vector getVector(String key, Vector defaultValue) {
592: try {
593: Object value = this .getValueFromJNDI(key);
594: if (value != null) {
595: Vector v = new Vector(1);
596: v.add(value.toString());
597: return v;
598: } else {
599: if (defaultValue == null) {
600: defaultValue = new Vector();
601: }
602: return defaultValue;
603: }
604: } catch (NoSuchElementException nsse) {
605: return defaultValue;
606: }
607: }
608:
609: /**
610: * @return String
611: */
612: public String getPrefix() {
613: return prefix;
614: }
615:
616: /**
617: * Sets the prefix.
618: * @param prefix The prefix to set
619: */
620: public void setPrefix(String prefix) {
621: this .prefix = prefix;
622: }
623:
624: private Object getValueFromJNDI(String key) {
625: if (clearedProperties.contains(key)) {
626: return null;
627: }
628: try {
629: key = StringUtils.replace(key, ".", "/");
630: return getContext().lookup(key);
631: } catch (java.util.NoSuchElementException nsse) {
632: return null;
633: } catch (NamingException ne) {
634: return null;
635: }
636: }
637:
638: private Context getContext() throws NamingException {
639: if (envCtx == null) {
640: Context initCtx = new InitialContext();
641: envCtx = (Context) initCtx.lookup(getPrefix());
642: }
643: return envCtx;
644: }
645: }
|