View Javadoc
1   /*
2    * Copyright 2007 Ralf Joachim
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   * $Id: Configuration.java 6907 2007-03-28 21:24:52Z rjoachim $
17   */
18  package org.castor.core.util;
19  
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.URL;
25  import java.text.MessageFormat;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Properties;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  /**
36   * Abstract base class to hold Castor configuration properties.
37   * 
38   * @version $Id: Configuration.java,v 1.8 2006/03/08 17:25:52 jens Exp $
39   * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
40   * @since 1.1.3
41   */
42  public abstract class AbstractProperties {
43  
44      /**
45       * Name of the system property that can be used to specify the location
46       * of user properties.
47       */
48      private static final String USER_PROPERTIES_SYSTEM_PROPERTY = 
49          "org.castor.user.properties.location";
50  
51      /**
52       * The <a href="http://jakarta.apache.org/commons/logging/">Jakarta Commons
53       * Logging</a> instance used for all logging.
54       */
55      private static final Log LOG = LogFactory.getLog(AbstractProperties.class);
56      
57      /**
58       * {@link ClassLoader} to be used for all classes of Castor and its required
59       * libraries.
60       */
61      private final ClassLoader _applicationClassLoader;
62      
63      /**
64       * {@link ClassLoader} to be used for all domain objects that are
65       * marshalled/unmarshalled or loaded from the database.
66       */
67      private final ClassLoader _domainClassLoader;
68      
69      /** 
70       * Parent properties. 
71       */
72      private final AbstractProperties _parent;
73      
74      private final Map _map = new HashMap();
75      
76      /**
77       * Default constructor. Application and domain class loaders will be initialized to the one
78       * used to load the concrete properties class. No parent properties will be set.
79       */
80      protected AbstractProperties() {
81          this(null, null);
82      }
83      
84      /**
85       * Construct properties that uses the specified class loaders. No parent properties will be set.
86       * 
87       * @param app Classloader to be used for all classes of Castor and its required libraries.
88       * @param domain Classloader to be used for all domain objects.
89       */
90      protected AbstractProperties(final ClassLoader app, final ClassLoader domain) {
91          _applicationClassLoader = (app != null) ? app : getClass().getClassLoader();
92          _domainClassLoader = (domain != null) ? domain : getClass().getClassLoader();
93          
94          _parent = null;
95      }
96      
97      /**
98       * Construct properties with given parent. Application and domain class loaders will be
99       * initialized to the ones of the parent. 
100      * 
101      * @param parent Parent properties.
102      */
103     protected AbstractProperties(final AbstractProperties parent) {
104         _applicationClassLoader = parent.getApplicationClassLoader();
105         _domainClassLoader = parent.getDomainClassLoader();
106         
107         _parent = parent;
108     }
109     
110     /**
111      * Get classloader to be used for all classes of Castor and its required libraries.
112      * 
113      * @return Classloader to be used for all classes of Castor and its required libraries.
114      */
115     public final ClassLoader getApplicationClassLoader() {
116         return _applicationClassLoader;
117     }
118     
119     /**
120      * Get classloader to be used for all domain objects that are marshalled/unmarshalled or
121      * loaded from the database.
122      * 
123      * @return Classloader to be used for all domain objects.
124      */
125     public final ClassLoader getDomainClassLoader() {
126         return _domainClassLoader;
127     }
128     
129     /**
130      * Load module properties from default locations.
131      * <br/>
132      * First it loads default properties contained in Castor JAR. This gets overwritten
133      * by a properties found on Java library directory. If no properties could be found
134      * until that point a PropertiesException will be thrown.
135      * 
136      * @param path Path to the default properties to load.
137      * @param filename Name of the properties file.
138      */
139     protected void loadDefaultProperties(final String path, final String filename) {
140         Properties properties = new Properties();
141         
142         // Get default properties from the Castor JAR.
143         boolean inCastorJar = loadFromClassPath(properties, path + filename);
144 
145         // Get overriding properties from the Java library directory, ignore if not
146         // found. If found merge existing properties.
147         boolean inJavaLibDir = loadFromJavaHome(properties, filename);
148 
149         // Couldn't find properties in Castor jar nor Java library directory.
150         if (!inCastorJar && !inJavaLibDir) {
151             throw new PropertiesException("Failed to load properties: " + filename);
152         }
153         
154         _map.putAll(properties);
155     }
156     
157     /**
158      * Load common user properties from classpath root and current working directory.
159      * <br/>
160      * First it loads default properties contained in Castor JAR. This gets overwritten
161      * by properties found on Java library directory. If no properties could be found
162      * until that point a PropertiesException will be thrown. At last overriding
163      * properties are loaded from root of classpath or, if that could not be found, from
164      * local working directory.
165      * 
166      * @param filename Name of the properties file.
167      */
168     protected void loadUserProperties(final String filename) {
169         Properties properties = new Properties();
170         
171         // Get common properties from the classpath root, ignore if not found.
172         boolean userPropertiesLoaded = loadFromClassPath(properties, "/" + filename);
173         
174         // If not found on classpath root, either it doesn't exist, or "." is not part of
175         // the classpath, try looking at local working directory.
176         if (!userPropertiesLoaded) {
177             userPropertiesLoaded = loadFromWorkingDirectory(properties, filename);
178         }
179         
180         if (!userPropertiesLoaded) {
181             String property = System.getProperty(USER_PROPERTIES_SYSTEM_PROPERTY);
182             if (property != null && property.length() > 0) {
183                 File file = new File(property);
184                 if (file.exists()) {
185                     LOG.info("Loading custom Castor properties from " + file.getAbsolutePath());
186                     userPropertiesLoaded = loadFromFile(properties, file);
187                 } else {
188                     LOG.warn(file.getAbsolutePath() + " is not a valid file.");
189                 }
190             }
191         }
192                     
193         _map.putAll(properties);
194     }
195     
196     /**
197      * Load properties with given filename from classpath and merge them into the given properties.
198      * 
199      * @param properties Properties to merge the loaded ones into.
200      * @param filename Name of the properties file to load from classpath.
201      * @return <code>true</code> if properties could be loaded, <code>false</code> otherwise.
202      */
203     private boolean loadFromClassPath(final Properties properties, final String filename) {
204         InputStream classPathStream = null;
205         try {      
206             URL url = getClass().getResource(filename);
207             if (url != null) {
208                 classPathStream = url.openStream(); 
209                 properties.load(classPathStream);
210                 
211                 if (LOG.isDebugEnabled()) {
212                     LOG.debug("Properties loaded from classpath: " + filename);
213                 }
214                 
215                 return true;
216             }
217             return false;
218         } catch (Exception ex) {
219             LOG.warn("Failed to load properties from classpath: " + filename, ex);
220             return false;
221         } finally {
222             if (classPathStream != null) {
223                 try {
224                     classPathStream.close();
225                 } catch (IOException e) {
226                     LOG.warn("Failed to close properties from classpath: " + filename);
227                 }
228             }
229         }
230     }
231     
232     /**
233      * Load properties with given filename from Java library directory and merge them into
234      * the given properties.
235      * 
236      * @param properties Properties to merge the loaded ones into.
237      * @param filename Name of the properties file to load from Java library directory.
238      * @return <code>true</code> if properties could be loaded, <code>false</code> otherwise.
239      */
240     private boolean loadFromJavaHome(final Properties properties, final String filename) {
241         try {
242             String javaHome = System.getProperty("java.home");
243             if (javaHome == null) { return false; }
244             return loadFromFile(properties, new File(new File(javaHome, "lib"), filename));
245         } catch (SecurityException ex) {
246             LOG.warn("Security policy prevented access to system property 'java.home'.", ex);
247             return false;
248         }
249     }
250     
251     /**
252      * Load properties with given filename from local working directory and merge them into
253      * the given properties.
254      * 
255      * @param properties Properties to merge the loaded ones into.
256      * @param filename Name of the properties file to load from local working directory.
257      * @return <code>true</code> if properties could be loaded, <code>false</code> otherwise.
258      */
259     private boolean loadFromWorkingDirectory(final Properties properties, final String filename) {
260         return loadFromFile(properties, new File(filename));
261     }
262     
263     /**
264      * Load properties with given file and merge them into the given properties.
265      * 
266      * @param properties Properties to merge the loaded ones into.
267      * @param file Properties file to load.
268      * @return <code>true</code> if properties could be loaded, <code>false</code> otherwise.
269      */
270     private boolean loadFromFile(final Properties properties, final File file) {
271         InputStream fileStream = null;
272         try {      
273             if (file.exists() && file.canRead()) {
274                 fileStream = new FileInputStream(file); 
275                 properties.load(fileStream);
276                 
277                 if (LOG.isDebugEnabled()) {
278                     LOG.debug("Properties file loaded: " + file);
279                 }
280                 
281                 return true;
282             }
283             return false;
284         } catch (SecurityException ex) {
285             LOG.warn("Security policy prevented access to properties file: " + file, ex);
286             return false;
287         } catch (Exception ex) {
288             LOG.warn("Failed to load properties file: " + file, ex);
289             return false;
290         } finally {
291             if (fileStream != null) {
292                 try {
293                     fileStream.close();
294                 } catch (IOException e) {
295                     LOG.warn("Failed to close properties file: " + file);
296                 }
297             }
298         }
299     }
300     
301     /**
302      * Put given value associated with given key into the properties map of this properties. If
303      * the properties previously associated the key to another value the previous value will be
304      * returned. If a mapping for the key previously exist in the parent properties only, the
305      * method returns <code>null</code> and not the value of the parent. This allows to distingush
306      * if the mapping existed in this properties or one of its parents.
307      * <br/>
308      * Putting a value in this properties does not change the value of its parent but the
309      * parents value isn't visible any more as it gets overwritten by this properties one.
310      * While this allows to redefine the value of a property it isn't allowed to undefine it.
311      * Therefore a <code>NullPointerException</code> will be thrown if the given value is
312      * <code>null</code>.
313      * 
314      * @param key Key of the property to put into properties.
315      * @param value Value to put into properties associated with the given key..
316      * @return Object in this properties that previously has been associated with the given key.
317      */
318     public final synchronized Object put(final String key, final Object value) {
319         if (value == null) { throw new NullPointerException(); }
320         return _map.put(key, value);
321     }
322     
323     /**
324      * Remove any value previously associated with the given key from this properties. The value
325      * previously associated with the key in this properties will be returned. If a mapping
326      * for the key existed in the parent properties only, the method returns <code>null</code>
327      * and not the value of the parent. This allows to distingush if the mapping existed in this
328      * properties or one of its parents.
329      * <br/>
330      * Removing the value from this properties does not mean that consecutive gets return
331      * <code>null</code> as one of the parents may still contain a mapping for the key that
332      * was hidden by the mapping in this properties.
333      * 
334      * @param key Key of the property to remove from properties.
335      * @return Object in this properties that previously has been associated with the given key.
336      */
337     public final synchronized Object remove(final String key) {
338         return _map.remove(key);
339     }
340     
341     /**
342      * Searches for the property with the specified key in this property map. If the key is not
343      * found in this property map, the parent property map, and its parents, recursively, are then
344      * checked.
345      * <br/>
346      * If the key maps to any object value, it will be returned as is. If the property is not found,
347      * <code>null</code> will be returned.
348      *
349      * @param key Key of the property to get from properties.
350      * @return Object in this property map with the specified key value.
351      */
352     protected synchronized Object get(final String key) {
353         Object value = _map.get(key);
354         if ((value == null) && (_parent != null)) {
355             value = _parent.get(key);
356         }
357         return value;
358     }
359     
360     /**
361      * Searches for the property with the specified key in this property map. If the key is not
362      * found in this property map, the parent property map, and its parents, recursively, are then
363      * checked.
364      * <br/>
365      * If the key maps to a boolean value, it will be returned as is. For string values that are
366      * equal, ignore case, to 'true' or 'false', the respective boolean value will be returned. If
367      * the property is not found, <code>null</code> will be returned. For all other types and
368      * string values a PropertiesException will be thrown. This behaviour is intended for those
369      * usecases that need distinguish between values that are missconfigured or not specified at
370      * all.
371      *
372      * @param key Property key.
373      * @return Boolean value in this property map with the specified key value.
374      */
375     public final Boolean getBoolean(final String key) {
376         Object objectValue = get(key);
377         
378         if (objectValue == null) {
379             return null;
380         } else if (objectValue instanceof Boolean) {
381             return (Boolean) objectValue;
382         } else if (objectValue instanceof String) {
383             String stringValue = (String) objectValue;
384             if ("true".equalsIgnoreCase(stringValue)) {
385                 return Boolean.TRUE;
386             } else if ("false".equalsIgnoreCase(stringValue)) {
387                 return Boolean.FALSE;
388             }
389         }
390         
391         Object[] args = new Object[] {key, objectValue};
392         String msg = "Properties value can not be converted to boolean: {0}={1}";
393         throw new PropertiesException(MessageFormat.format(msg, args));
394     }
395     
396     /**
397      * Searches for the property with the specified key in this property map. If the key is not
398      * found in this property map, the parent property map, and its parents, recursively, are then
399      * checked.
400      * <br/>
401      * If the key maps to a boolean value, it will be returned as is. For string values that are
402      * equal, ignore case, to 'true' or 'false', the respective boolean value will be returned. In
403      * all other cases the given default value will be returned.
404      *
405      * @param key Property key.
406      * @param defaultValue Default value.
407      * @return Boolean value in this property map with the specified key value.
408      */
409     public final boolean getBoolean(final String key, final boolean defaultValue) {
410         Object objectValue = get(key);
411         
412         if (objectValue instanceof Boolean) {
413             return ((Boolean) objectValue).booleanValue();
414         } else if (objectValue instanceof String) {
415             String stringValue = (String) objectValue;
416             if ("true".equalsIgnoreCase(stringValue)) {
417                 return true;
418             } else if ("false".equalsIgnoreCase(stringValue)) {
419                 return false;
420             }
421         }
422         
423         return defaultValue;
424     }
425     
426     /**
427      * Searches for the property with the specified key in this property map. If the key is not
428      * found in this property map, the parent property map, and its parents, recursively, are then
429      * checked.
430      * <br/>
431      * If the key maps to a integer value, it will be returned as is. For string values that can
432      * be interpreted as signed decimal integer, the respective integer value will be returned. If
433      * the property is not found, <code>null</code> will be returned. For all other types and
434      * string values a PropertiesException will be thrown. This behaviour is intended for those
435      * usecases that need distinguish between values that are missconfigured or not specified at
436      * all.
437      *
438      * @param key Property key.
439      * @return Integer value in this property map with the specified key value.
440      */
441     public final Integer getInteger(final String key) {
442         Object objectValue = get(key);
443         
444         if (objectValue == null) {
445             return null;
446         } else if (objectValue instanceof Integer) {
447             return (Integer) objectValue;
448         } else if (objectValue instanceof String) {
449             try {
450                 return Integer.valueOf((String) objectValue);
451             } catch (NumberFormatException ex) {
452                 Object[] args = new Object[] {key, objectValue};
453                 String msg = "Properties value can not be converted to int: {0}={1}";
454                 throw new PropertiesException(MessageFormat.format(msg, args), ex);
455             }
456         }
457         
458         Object[] args = new Object[] {key, objectValue};
459         String msg = "Properties value can not be converted to int: {0}={1}";
460         throw new PropertiesException(MessageFormat.format(msg, args));
461     }
462 
463     /**
464      * Searches for the property with the specified key in this property map. If the key is not
465      * found in this property map, the parent property map, and its parents, recursively, are then
466      * checked.
467      * <br/>
468      * If the key maps to a integer value, it will be returned as is. For string values that can
469      * be interpreted as signed decimal integer, the respective integer value will be returned. In
470      * all other cases the given default value will be returned.
471      *
472      * @param key Property key.
473      * @param defaultValue Default value.
474      * @return Integer value in this property map with the specified key value.
475      */
476     public final int getInteger(final String key, final int defaultValue) {
477         Object objectValue = get(key);
478         
479         if (objectValue instanceof Integer) {
480             return ((Integer) objectValue).intValue();
481         } else if (objectValue instanceof String) {
482             String stringValue = (String) objectValue;
483             try {
484                 return Integer.parseInt(stringValue);
485             } catch (NumberFormatException ex) {
486                 return defaultValue;
487             }
488         }
489 
490         return defaultValue;
491     }
492 
493     /**
494      * Searches for the property with the specified key in this property map. If the key is not
495      * found in this property map, the parent property map, and its parents, recursively, are then
496      * checked.
497      * <br/>
498      * If the key maps to a string value, it will be returned as is. If the property is not found,
499      * <code>null</code> will be returned. For all other types a PropertiesException will be
500      * thrown.
501      *
502      * @param key Property key.
503      * @return String value in this property map with the specified key value.
504      */
505     public final String getString(final String key) {
506         Object objectValue = get(key);
507         
508         if (objectValue == null) {
509             return null;
510         } else if (objectValue instanceof String) {
511             return (String) objectValue;
512         }
513         
514         Object[] args = new Object[] {key, objectValue};
515         String msg = "Properties value is not a string: {0}={1}";
516         throw new PropertiesException(MessageFormat.format(msg, args));
517     }
518     
519     /**
520      * Searches for the property with the specified key in this property map. If the key is not
521      * found in this property map, the parent property map, and its parents, recursively, are then
522      * checked.
523      * <br/>
524      * If the key maps to a string value that is not empty, it will be returned as is. In all other
525      * cases the given default value will be returned.
526      *
527      * @param key Property key.
528      * @param defaultValue Default value.
529      * @return String value in this property map with the specified key value.
530      */
531     public final String getString(final String key, final String defaultValue) {
532         Object objectValue = get(key);
533         
534         if ((objectValue instanceof String) && !"".equals(objectValue)) {
535             return (String) objectValue;
536         }
537         
538         return defaultValue;
539     }
540     
541     /**
542      * Searches for the property with the specified key in this property map. If the key is not
543      * found in this property map, the parent property map, and its parents, recursively, are then
544      * checked.
545      * <br/>
546      * If the key maps to a string array, it will be returned as is. A simple string will be
547      * converted into a string array by splitting it into substrings at every occurence of ','
548      * character. If the property is not found, <code>null</code> will be returned. For all other
549      * types a PropertiesException will be thrown.
550      *
551      * @param key Property key.
552      * @return String array in this property map with the specified key value.
553      */
554     public final String[] getStringArray(final String key) {
555         Object objectValue = get(key);
556         
557         if (objectValue == null) {
558             return null;
559         } else if (objectValue instanceof String[]) {
560             return (String[]) objectValue;
561         } else if (objectValue instanceof String) {
562             return ((String) objectValue).split(",");
563         }
564 
565         Object[] args = new Object[] {key, objectValue};
566         String msg = "Properties value is not a String[]: {0}={1}";
567         throw new PropertiesException(MessageFormat.format(msg, args));
568     }
569     
570     /**
571      * Searches for the property with the specified key in this property map. If the key is not
572      * found in this property map, the parent property map, and its parents, recursively, are then
573      * checked.
574      * <br/>
575      * If the key maps to a class, it will be returned as is. A simple string will be interpreted
576      * as class name of which the class will be loaded with the given class loader. If the property
577      * is not found, <code>null</code> will be returned. For all other types and if loading of the
578      * class fails a PropertiesException will be thrown.
579      *
580      * @param key Property key.
581      * @param loader Class loader to load classes with.
582      * @return Class in this property map with the specified key value.
583      */
584     public final Class getClass(final String key, final ClassLoader loader) {
585         Object objectValue = get(key);
586         
587         if (objectValue == null) {
588             return null;
589         } else if (objectValue instanceof Class) {
590             return (Class) objectValue;
591         } else if (objectValue instanceof String) {
592             String classname = (String) objectValue;
593             try {
594                 return loader.loadClass(classname);
595             } catch (ClassNotFoundException ex) {
596                 Object[] args = new Object[] {key, classname};
597                 String msg = "Could not find class of properties value: {0}={1}";
598                 throw new PropertiesException(MessageFormat.format(msg, args), ex);
599             }
600         }
601 
602         Object[] args = new Object[] {key, objectValue};
603         String msg = "Properties value is not a Class: {0}={1}";
604         throw new PropertiesException(MessageFormat.format(msg, args));
605     }
606     
607     /**
608      * Searches for the property with the specified key in this property map. If the key is not
609      * found in this property map, the parent property map, and its parents, recursively, are then
610      * checked.
611      * <br/>
612      * If the key maps to a class array, it will be returned as is. A simple string will be
613      * splitted it into substrings at every occurence of ',' character. Each of these substrings
614      * will interpreted as class name of which the class will be loaded with the given class
615      * loader. If the property is not found, <code>null</code> will be returned. For all other
616      * types and if loading of one of the classes fails a PropertiesException will be thrown.
617      *
618      * @param key Property key.
619      * @param loader Class loader to load classes with.
620      * @return Class array in this property map with the specified key value.
621      */
622     public final Class[] getClassArray(final String key, final ClassLoader loader) {
623         Object objectValue = get(key);
624         
625         if (objectValue == null) {
626             return null;
627         } else if (objectValue instanceof Class[]) {
628             return (Class[]) objectValue;
629         } else if (objectValue instanceof String) {
630             String[] classnames = ((String) objectValue).split(",");
631             Class[] classes = new Class[classnames.length];
632             for (int i = 0; i < classnames.length; i++) {
633                 try {
634                     classes[i] = loader.loadClass(classnames[i]);
635                 } catch (ClassNotFoundException ex) {
636                     Object[] args = new Object[] {key, new Integer(i), classnames[i]};
637                     String msg = "Could not find class of properties value: {0}[{1}]={2}";
638                     throw new PropertiesException(MessageFormat.format(msg, args), ex);
639                 }
640             }
641             return classes;
642         }
643 
644         Object[] args = new Object[] {key, objectValue};
645         String msg = "Properties value is not a Class[]: {0}={1}";
646         throw new PropertiesException(MessageFormat.format(msg, args));
647     }
648     
649     /**
650      * Searches for the property with the specified key in this property map. If the key is not
651      * found in this property map, the parent property map, and its parents, recursively, are then
652      * checked.
653      * <br/>
654      * If the key maps to any object value, it will be returned as is. If the property is not found,
655      * <code>null</code> will be returned.
656      *
657      * @param key Property key.
658      * @return Object in this property map with the specified key value.
659      */
660     public final Object getObject(final String key) {
661         return get(key);
662     }
663     
664     /**
665      * Searches for the property with the specified key in this property map. If the key is not
666      * found in this property map, the parent property map, and its parents, recursively, are then
667      * checked.
668      * <br/>
669      * If the key maps to a object array, it will be returned as is. A simple string will be
670      * splitted it into substrings at every occurence of ',' character. Each of these substrings
671      * will interpreted as class name of which the class will be loaded with the given class
672      * loader and instantiated using its default constructor. If the property is not found,
673      * <code>null</code> will be returned. For all other types and if loading or instantiation of
674      * one of the classes fails a PropertiesException will be thrown.
675      *
676      * @param key Property key.
677      * @param loader Class loader to load classes with.
678      * @return Class array in this property map with the specified key value.
679      */
680     public final Object[] getObjectArray(final String key, final ClassLoader loader) {
681         Object objectValue = get(key);
682         
683         if (objectValue == null) {
684             return null;
685         } else if (objectValue instanceof Object[]) {
686             return (Object[]) objectValue;
687         } else if (objectValue instanceof String) {
688             List objects = new ArrayList();
689             String[] classnames = ((String) objectValue).split(",");
690             for (int i = 0; i < classnames.length; i++) {
691                 String classname = classnames[i];
692                 try {
693                     if ((classname != null) && !"".equals(classname.trim())) {
694                         classname = classname.trim();
695                         objects.add(loader.loadClass(classname).newInstance());
696                     }
697                 } catch (ClassNotFoundException ex) {
698                     Object[] args = new Object[] {key, new Integer(i), classname};
699                     String msg = "Could not find configured class: {0}[{1}]={2}";
700                     throw new PropertiesException(MessageFormat.format(msg, args), ex);
701                 } catch (IllegalAccessException ex) {
702                     Object[] args = new Object[] {key, new Integer(i), classname};
703                     String msg = "Could not instantiate configured class: {0}[{1}]={2}";
704                     throw new PropertiesException(MessageFormat.format(msg, args), ex);
705                 } catch (InstantiationException ex) {
706                     Object[] args = new Object[] {key, new Integer(i), classname};
707                     String msg = "Could not instantiate configured class: {0}[{1}]={2}";
708                     throw new PropertiesException(MessageFormat.format(msg, args), ex);
709                 } catch (ExceptionInInitializerError ex) {
710                     Object[] args = new Object[] {key, new Integer(i), classname};
711                     String msg = "Could not instantiate configured class: {0}[{1}]={2}";
712                     throw new PropertiesException(MessageFormat.format(msg, args), ex);
713                 } catch (SecurityException ex) {
714                     Object[] args = new Object[] {key, new Integer(i), classname};
715                     String msg = "Could not instantiate configured class: {0}[{1}]={2}";
716                     throw new PropertiesException(MessageFormat.format(msg, args), ex);
717                 }
718             }
719             return objects.toArray();
720         }
721 
722         Object[] args = new Object[] {key, objectValue};
723         String msg = "Properties value is not an Object[]: {0}={1}";
724         throw new PropertiesException(MessageFormat.format(msg, args));
725     }
726     
727 }