View Javadoc

1   /*
2    * Copyright 2004-2005 Germinus XXI
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License")
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.germinus.easyconf;
17  
18  import java.math.BigDecimal;
19  import java.math.BigInteger;
20  import java.util.*;
21  
22  import org.apache.commons.beanutils.DynaBean;
23  import org.apache.commons.configuration.beanutils.ConfigurationDynaBean;
24  import org.apache.commons.configuration.CompositeConfiguration;
25  import org.apache.commons.configuration.Configuration;
26  import org.apache.commons.configuration.ConfigurationConverter;
27  import org.apache.commons.configuration.DataConfiguration;
28  import org.apache.commons.configuration.MapConfiguration;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  
32  /***
33   * Part of a component configuration which contains its properties.
34   * 
35   * The properties can be accessed by type and automatic conversion will be
36   * performed. The supported types are: BigDecimal, BigInteger, Boolean, Byte,
37   * Double, Float, Integer, List, Long, Short, String and StringArray
38   * 
39   * It is based on the <code>Configuration</code> interface from Jakarta
40   * Commons Configuration but it is given a different name which makes more sense
41   * inside EasyConf.
42   * 
43   * The boolean flag throwExceptionOnMissing controls the behaviour of this class
44   * when a property that does not exist is queried. If set to true (the default)
45   * a <tt>NoSuchElementException</tt> will be thrown if the given key does not
46   * exist and no default was provided. If set to false, <tt>null</tt> will be
47   * returned except for the method getList() which will return an empty
48   * unmodifyiable list.
49   * 
50   * @author Jorge Ferrer
51   * @version $Revision: 1.22 $
52   *  
53   */
54  public class ComponentProperties {
55  
56      public static final String NULL_STRING = null;
57  
58      private static final List EMPTY_LIST = Collections
59              .unmodifiableList(new ArrayList());
60  
61      private static final Log log = LogFactory.getLog(ComponentProperties.class);
62  
63      AggregatedProperties properties;
64  
65      ComponentProperties(AggregatedProperties conf) {
66          this.properties = conf;
67          setThrowExceptionOnMissing(false);
68      }
69  
70      // .................. Conversion methods ...................
71  
72      /***
73       * Returns a decorator of the configuration that implements the Map
74       * interface. Note that any changes made to this decorator will be made to
75       * the original configuration and viceversa.
76       * 
77       * @return a <code>java.util.Map</code> instance
78       */
79      public Map toMap() {
80          return ConfigurationConverter.getMap(properties);
81      }
82  
83      /***
84       * Returns a decorator of the configuration that can be used as a DynaBean.
85       * Note that any changes made to this decorator will be made to the original
86       * configuration and viceversa.
87       * 
88       * @return a <code>DynaBean</code> instance
89       */
90      public DynaBean toDynaBean() {
91          return new ConfigurationDynaBean(properties);
92      }
93  
94      /***
95       * Returns a decorator of the configuration of type
96       * <tt>org.apache.commons.configuration.Configuration</tt> Note that any
97       * changes made to this decorator will be made to the original configuration
98       * and viceversa.
99       * 
100      * @return a <code>Configuration</code> instance
101      */
102     public Configuration toConfiguration() {
103         return properties;
104     }
105 
106     /***
107      * Returns a decorator of the configuration of type
108      * <tt>org.apache.commons.configuration.DataConfiguration</tt>. This
109      * decorator has many extra methods for retrieving typed properties such as
110      * color, URL, locale, Calendar and lists and array of any of these types.
111      * 
112      * Note that any changes made to this decorator will be made to the original
113      * configuration and viceversa.
114      * 
115      * @return a <code>DataConfiguration</code> instance
116      */
117     public DataConfiguration toDataConfiguration() {
118         return new DataConfiguration(properties);
119     }
120 
121     /***
122      * Returns a <b>copy </b> of the configuration into a
123      * <tt>java.util.Properties</tt> class. Multivalued properties will be
124      * converted to comma-separated strings. Any changes made to the
125      * configuration will <b>not </b>be available to the Properties and
126      * viceversa.
127      * 
128      * @return a <code>Properties</code> instance
129      */
130     public java.util.Properties getProperties() {
131         return ConfigurationConverter.getProperties(properties);
132     }
133 
134     public Object getProperty(String key) {
135         return properties.getProperty(key);
136     }
137 
138     // ..................... Delegated methods ...............
139 
140     public boolean containsKey(String key) {
141         return properties.containsKey(key);
142     }
143 
144     public boolean equals(Object obj) {
145         return properties.equals(obj);
146     }
147 
148     public Iterator getKeys() {
149         return properties.getKeys();
150     }
151 
152     public Iterator getKeys(String prefix) {
153         return properties.getKeys(prefix);
154     }
155 
156     public int hashCode() {
157         return properties.hashCode();
158     }
159 
160     public boolean isEmpty() {
161         return properties.isEmpty();
162     }
163 
164     public void setProperty(String key, Object value) {
165         properties.setProperty(key, value);
166     }
167 
168     public Configuration subset(String prefix) {
169         return properties.subset(prefix);
170     }
171 
172     public String toString() {
173         return properties.toString();
174     }
175 
176     // ..................... BigDecimal ......................
177 
178     public BigDecimal getBigDecimal(String key) {
179         return properties.getBigDecimal(key);
180     }
181 
182     public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
183         return properties.getBigDecimal(key, defaultValue);
184     }
185 
186     public BigDecimal getBigDecimal(String key, Filter filter) {
187         BigDecimal value = getBigDecimal(key, filter, null);
188         validateValue(key, value);
189         return value;
190     }
191 
192     public BigDecimal getBigDecimal(String key, Filter filter,
193                                     BigDecimal defaultValue) {
194         return (BigDecimal) getPropertyWithFilter(key, filter,
195                 BigDecimal.class, defaultValue);
196     }
197 
198     // ..................... BigInteger ......................
199 
200     public BigInteger getBigInteger(String key) {
201         return properties.getBigInteger(key);
202     }
203 
204     public BigInteger getBigInteger(String key, BigInteger defaultValue) {
205         return properties.getBigInteger(key, defaultValue);
206     }
207 
208     public BigInteger getBigInteger(String key, Filter filter) {
209         BigInteger value = getBigInteger(key, filter, null);
210         validateValue(key, value);
211         return value;
212     }
213 
214     public BigInteger getBigInteger(String key, Filter filter,
215                                     BigInteger defaultValue) {
216         return (BigInteger) getPropertyWithFilter(key, filter,
217                 BigInteger.class, defaultValue);
218     }
219 
220     // ..................... Boolean ......................
221 
222     public boolean getBoolean(String key) {
223         return properties.getBoolean(key);
224     }
225 
226     public boolean getBoolean(String key, boolean defaultValue) {
227         return properties.getBoolean(key, defaultValue);
228     }
229 
230     public Boolean getBoolean(String key, Boolean defaultValue)
231             throws NoClassDefFoundError {
232         return properties.getBoolean(key, defaultValue);
233     }
234 
235     public boolean getBoolean(String key, Filter filter) {
236         Boolean value = getBoolean(key, filter, null);
237         validateValue(key, value);
238         return value.booleanValue();
239     }
240 
241     public Boolean getBoolean(String key, Filter filter, Boolean defaultValue)
242             throws NoClassDefFoundError {
243         return (Boolean) getPropertyWithFilter(key, filter, Boolean.class,
244                 defaultValue);
245     }
246 
247     public boolean getBoolean(String key, Filter filter, boolean defaultValue) {
248         return getBoolean(key, filter, new Boolean(defaultValue))
249                 .booleanValue();
250     }
251 
252     // ..................... Byte ......................
253 
254     public byte getByte(String key) {
255         return properties.getByte(key);
256     }
257 
258     public byte getByte(String key, byte defaultValue) {
259         return properties.getByte(key, defaultValue);
260     }
261 
262     public Byte getByte(String key, Byte defaultValue) {
263         return properties.getByte(key, defaultValue);
264     }
265 
266     public byte getByte(String key, Filter filter) {
267         Byte value = getByte(key, filter, null);
268         validateValue(key, value);
269         return value.byteValue();
270     }
271 
272     public Byte getByte(String key, Filter filter, Byte defaultValue) {
273         return (Byte) getPropertyWithFilter(key, filter, Byte.class,
274                 defaultValue);
275     }
276 
277     public byte getByte(String key, Filter filter, byte defaultValue) {
278         return getByte(key, filter, new Byte(defaultValue)).byteValue();
279     }
280 
281     // ..................... Double ......................
282 
283     public double getDouble(String key) {
284         return properties.getDouble(key);
285     }
286 
287     public double getDouble(String key, double defaultValue) {
288         return properties.getDouble(key, defaultValue);
289     }
290 
291     public Double getDouble(String key, Double defaultValue) {
292         return properties.getDouble(key, defaultValue);
293     }
294 
295     public double getDouble(String key, Filter filter) {
296         Double value = getDouble(key, filter, null);
297         validateValue(key, value);
298         return value.doubleValue();
299     }
300 
301     public Double getDouble(String key, Filter filter, Double defaultValue) {
302         return (Double) getPropertyWithFilter(key, filter, Double.class,
303                 defaultValue);
304     }
305 
306     public double getDouble(String key, Filter filter, double defaultValue) {
307         return getDouble(key, filter, new Double(defaultValue)).doubleValue();
308     }
309 
310     // ..................... Class ......................
311 
312     /***
313      * Get the <code>Class</code> representation of the class name specified
314      * in the given property
315      * 
316      * @throws ClassNotFoundException
317      *             if the specified class is not found
318      */
319     public Class getClass(String key) throws ClassNotFoundException {
320         String className = getString(key);
321         return ClasspathUtil.locateClass(className);
322     }
323 
324     /***
325      * Get the <code>Class</code> representation of the class name specified
326      * in the given property. Or the <code>defaultValue</code> if no value has
327      * been given to the property.
328      * 
329      * @throws ClassNotFoundException
330      *             if a class has been configured but it is not found
331      */
332     public Class getClass(String key, Class defaultValue)
333             throws ClassNotFoundException {
334         String defaultStringValue = null;
335         String className = getString(key, defaultStringValue);
336         if (className == defaultStringValue) {
337             return defaultValue;
338         }
339         return ClasspathUtil.locateClass(className);
340     }
341 
342     /***
343      * Similar to the previous methods but complementing the property key with
344      * the given filter
345      */
346     public Class getClass(String key, Filter filter)
347             throws ClassNotFoundException {
348         String className = getString(key, filter);
349         return ClasspathUtil.locateClass(className);
350     }
351 
352     /***
353      * Equivalent to the previous method but giving a default value which will
354      * be used if no value has been specified in the configurations file
355      */
356     public Class getClass(String key, Filter filter, Class defaultValue)
357             throws ClassNotFoundException {
358         String defaultStringValue = null;
359         String className = getString(key, filter, null);
360         if (className == defaultStringValue) {
361             return defaultValue;
362         }
363         return ClasspathUtil.locateClass(className);
364     }
365 
366     // ..................... Class ......................
367 
368     /***
369      * Get an array of <code>Class</code> objects for the class names
370      * specified in the given property
371      * 
372      * @throws ClassNotFoundException
373      *             if the any of the configured classes is not found
374      */
375     public Class[] getClassArray(String key) throws ClassNotFoundException {
376         String[] classNames = getStringArray(key);
377         return ClasspathUtil.locateClasses(classNames);
378     }
379 
380     /***
381      * Get an array of <code>Class</code> objects for the class names
382      * specified in the given property. Or the <code>defaultValue</code> if no
383      * value has been given to the property.
384      * 
385      * @throws ClassNotFoundException
386      *             if the any of the configured classes is not found
387      */
388     public Class[] getClassArray(String key, Class[] defaultValue)
389             throws ClassNotFoundException {
390         String[] defaultStringArrayValue = null;
391         String[] classNames = getStringArray(key, defaultStringArrayValue);
392         if (classNames == defaultStringArrayValue) {
393             return defaultValue;
394         }
395         return ClasspathUtil.locateClasses(classNames);
396     }
397 
398     /***
399      * Similar to the previous methods but complementing the property key with
400      * the given filter
401      */
402     public Class[] getClassArray(String key, Filter filter)
403             throws ClassNotFoundException {
404         String[] classNames = getStringArray(key, filter);
405         return ClasspathUtil.locateClasses(classNames);
406     }
407 
408     /***
409      * Equivalent to the previous method but giving a default value which will
410      * be used if no value has been specified in the configurations file
411      */
412     public Class[] getClassArray(String key, Filter filter, Class[] defaultValue)
413             throws ClassNotFoundException {
414         String[] defaultStringArrayValue = null;
415         String[] classNames = getStringArray(key, filter,
416                 defaultStringArrayValue);
417         if (classNames == defaultStringArrayValue) {
418             return defaultValue;
419         }
420         return ClasspathUtil.locateClasses(classNames);
421     }
422 
423     // ..................... Float ......................
424 
425     public float getFloat(String key) {
426         return properties.getFloat(key);
427     }
428 
429     public float getFloat(String key, float defaultValue) {
430         return properties.getFloat(key, defaultValue);
431     }
432 
433     public Float getFloat(String key, Float defaultValue) {
434         return properties.getFloat(key, defaultValue);
435     }
436 
437     public float getFloat(String key, Filter filter) {
438         Float value = getFloat(key, filter, null);
439         validateValue(key, value);
440         return value.floatValue();
441     }
442 
443     public Float getFloat(String key, Filter filter, Float defaultValue) {
444         return (Float) getPropertyWithFilter(key, filter, Float.class,
445                 defaultValue);
446     }
447 
448     public float getFloat(String key, Filter filter, float defaultValue) {
449         return getFloat(key, filter, new Float(defaultValue)).floatValue();
450     }
451 
452     // ..................... Integer ......................
453 
454     public int getInt(String key) {
455         return properties.getInt(key);
456     }
457 
458     public int getInt(String key, int defaultValue) {
459         return properties.getInt(key, defaultValue);
460     }
461 
462     public Integer getInteger(String key, Integer defaultValue) {
463         return properties.getInteger(key, defaultValue);
464     }
465 
466     public int getInt(String key, Filter filter) {
467         Integer value = getInteger(key, filter, null);
468         validateValue(key, value);
469         return value.intValue();
470     }
471 
472     public Integer getInteger(String key, Filter filter, Integer defaultValue) {
473         return (Integer) getPropertyWithFilter(key, filter, Integer.class,
474                 defaultValue);
475     }
476 
477     public int getInt(String key, Filter filter, int defaultValue) {
478         return getInteger(key, filter, new Integer(defaultValue)).intValue();
479     }
480 
481     // ..................... List ......................
482 
483     public List getList(String key) {
484         return properties.getList(key);
485     }
486 
487     public List getList(String key, List defaultValue) {
488         return properties.getList(key, defaultValue);
489     }
490 
491     public List getList(String key, Filter filter) {
492         List value = (List) getPropertyWithFilter(key, filter, List.class, null);
493         validateValue(key, value);
494         if (value == null) {
495             value = EMPTY_LIST;
496         }
497         return value;
498     }
499 
500     public List getList(String key, Filter filter, List defaultValue) {
501         return (List) getPropertyWithFilter(key, filter, List.class,
502                 defaultValue);
503     }
504 
505     // ..................... Long ......................
506 
507     public long getLong(String key) {
508         return properties.getLong(key);
509     }
510 
511     public Long getLong(String key, Long defaultValue) {
512         return properties.getLong(key, defaultValue);
513     }
514 
515     public long getLong(String key, long defaultValue) {
516         return properties.getLong(key, defaultValue);
517     }
518 
519     public long getLong(String key, Filter filter) {
520         Long value = getLong(key, filter, null);
521         validateValue(key, value);
522         return value.longValue();
523     }
524 
525     public Long getLong(String key, Filter filter, Long defaultValue) {
526         return (Long) getPropertyWithFilter(key, filter, Long.class,
527                 defaultValue);
528     }
529 
530     public long getLong(String key, Filter filter, long defaultValue) {
531         return getLong(key, filter, new Long(defaultValue)).longValue();
532     }
533 
534     // ..................... Short ......................
535 
536     public short getShort(String key) {
537         return properties.getShort(key);
538     }
539 
540     public Short getShort(String key, Short defaultValue) {
541         return properties.getShort(key, defaultValue);
542     }
543 
544     public short getShort(String key, short defaultValue) {
545         return properties.getShort(key, defaultValue);
546     }
547 
548     public short getShort(String key, Filter filter) {
549         Short value = getShort(key, filter, null);
550         validateValue(key, value);
551         return value.shortValue();
552     }
553 
554     public Short getShort(String key, Filter filter, Short defaultValue) {
555         return (Short) getPropertyWithFilter(key, filter, Short.class,
556                 defaultValue);
557     }
558 
559     public short getShort(String key, Filter filter, short defaultValue) {
560         return getShort(key, filter, new Short(defaultValue)).shortValue();
561     }
562 
563     // ..................... String ......................
564 
565     /***
566      * Get the String value of the given key. If it contains a list of values,
567      * they will be serialized to a comma-separated format (use getList or
568      * getStringArray if you want separated list items)
569      */
570     public String getString(String key) {
571         Object value = properties.getProperty(key);
572         String result;
573         if (value instanceof List) {
574             result = listToString((List) value);
575         } else {
576             result = properties.getString(key);
577         }
578         return result;
579         //		return properties.getString(key);
580     }
581 
582     public String getString(String key, String defaultValue) {
583         return properties.getString(key, defaultValue);
584     }
585 
586     public String getString(String key, Filter filter) {
587         String value = getString(key, filter, null);
588         validateValue(key, value);
589         return value;
590     }
591 
592     public String getString(String key, Filter filter, String defaultValue) {
593         return (String) getPropertyWithFilter(key, filter, String.class,
594                 defaultValue);
595     }
596 
597     // ..................... StringArray ......................
598 
599     public String[] getStringArray(String key) {
600         return properties.getStringArray(key);
601     }
602 
603     public String[] getStringArray(String key, Filter filter) {
604         List value = getList(key, filter);
605         return (String[]) value.toArray(new String[0]);
606     }
607 
608     public String[] getStringArray(String key, Filter filter,
609                                    String[] defaultValue) {
610         List defaultList = null;
611         if (defaultValue != null) {
612             defaultList = Arrays.asList(defaultValue);
613         }
614         List value = getList(key, filter, defaultList);
615         if (value == null) { //This should never happen
616             return null;
617         } else {
618             return (String[]) value.toArray(new String[0]);
619         }
620     }
621 
622     public String[] getStringArray(String key, String[] defaultValue) {
623         List defaultList = null;
624         if (defaultValue != null) {
625             defaultList = Arrays.asList(defaultValue);
626         }
627         List value = getList(key, defaultList);
628         if (value == null) {
629             return null;
630         } else {
631             return (String[]) value.toArray(new String[0]);
632         }
633     }
634 
635     // ................. Control methods .....................
636 
637     public boolean hasBaseConfiguration() {
638         return properties.hasBaseConfiguration();
639     }
640 
641     /***
642      * Get a list of the sources which have been loaded for this component
643      */
644     public List getLoadedSources() {
645         return properties.loadedSources();
646     }
647 
648     /***
649      * Set the flag throwExceptionOnMissing. See the class documentation for
650      * more details.
651      */
652     public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing) {
653         properties.setThrowExceptionOnMissing(throwExceptionOnMissing);
654     }
655 
656     public boolean isThrowExceptionOnMissing() {
657         return properties.isThrowExceptionOnMissing();
658     }
659 
660     /***
661      * Returned the configured delay period for this component or null if
662      * reloading is not being performed
663      */
664     public Long getDelayPeriod() {
665         Long nullLong = null;
666         return getLong(Conventions.RELOAD_DELAY_PROPERTY, nullLong);
667     }
668 
669     public String getComponentName() {
670         return properties.getComponentName();
671     }
672 
673     // ............. Helper methods ......................
674 
675     protected Object getPropertyWithFilter(String key, Filter filter,
676                                            Class theClass, Object defaultValue) {
677         CompositeConfiguration filteredConf = properties;
678         Object value = null;
679         for (int i = filter.numOfSelectors(); (i >= 0) && (value == null); i--) {
680             MapConfiguration varsConf = null;
681             if (filter.hasVariables()) {
682                 varsConf = new MapConfiguration(filter.getVariables());
683                 filteredConf = new CompositeConfiguration();
684                 filteredConf.addConfiguration(properties);
685                 filteredConf.addConfiguration(varsConf);
686             }
687             value = getTypedPropertyWithDefault(
688                     key + filter.getFilterSuffix(i), theClass, filteredConf,
689                     null);
690             if (varsConf != null) {
691                 properties.removeConfiguration(varsConf);
692             }
693             log.debug("Value for " + key + filter.getFilterSuffix(i) + "="
694                     + value);
695         }
696 		if (value == null) {
697 			value = defaultValue;
698 		}
699         return value;
700     }
701 
702     protected static Object getTypedPropertyWithDefault(
703                                                         String key,
704                                                         Class theClass,
705                                                         Configuration properties,
706                                                         Object defaultValue) {
707         if (theClass.equals(Float.class)) {
708             return properties.getFloat(key, (Float) defaultValue);
709 
710         } else if (theClass.equals(Integer.class)) {
711             return properties.getInteger(key, (Integer) defaultValue);
712 
713         } else if (theClass.equals(String.class)) {
714             return properties.getString(key, (String) defaultValue);
715 
716         } else if (theClass.equals(Double.class)) {
717             return properties.getDouble(key, (Double) defaultValue);
718 
719         } else if (theClass.equals(Long.class)) {
720             return properties.getLong(key, (Long) defaultValue);
721 
722         } else if (theClass.equals(Boolean.class)) {
723             return properties.getBoolean(key, (Boolean) defaultValue);
724 
725         } else if (theClass.equals(List.class)) {
726             return properties.getList(key, (List) defaultValue);
727 
728         } else if (theClass.equals(BigInteger.class)) {
729             return properties.getBigInteger(key, (BigInteger) defaultValue);
730 
731         } else if (theClass.equals(BigDecimal.class)) {
732             return properties.getBigDecimal(key, (BigDecimal) defaultValue);
733 
734         } else if (theClass.equals(Byte.class)) {
735             return properties.getByte(key, (Byte) defaultValue);
736 
737         } else if (theClass.equals(Short.class)) {
738             return properties.getShort(key, (Short) defaultValue);
739         }
740         throw new IllegalArgumentException("Class " + theClass + " is not"
741                 + "supported for properties");
742     }
743 
744     protected void validateValue(String key, Object value) {
745         if ((value == null) && isThrowExceptionOnMissing()) {
746             throw new NoSuchElementException("Property with key=" + key
747                     + " was not found");
748         }
749     }
750 
751     protected String listToString(List list) {
752         StringBuffer property = new StringBuffer();
753         Iterator it = list.iterator();
754         while (it.hasNext()) {
755             property.append(String.valueOf(it.next()));
756             if (it.hasNext()) {
757                 property.append(",");
758             }
759         }
760         return property.toString();
761     }
762 }
763