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.io.File;
20 import java.net.URL;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27
28 import org.apache.commons.configuration.beanutils.BeanDeclaration;
29 import org.apache.commons.configuration.beanutils.BeanFactory;
30 import org.apache.commons.configuration.beanutils.BeanHelper;
31 import org.apache.commons.configuration.beanutils.DefaultBeanFactory;
32 import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;
33 import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
34 import org.apache.commons.configuration.tree.ConfigurationNode;
35 import org.apache.commons.configuration.tree.DefaultExpressionEngine;
36 import org.apache.commons.configuration.tree.OverrideCombiner;
37 import org.apache.commons.configuration.tree.UnionCombiner;
38 import org.apache.commons.lang.text.StrLookup;
39 import org.apache.commons.logging.LogFactory;
40
41 /***
42 * <p>
43 * A factory class that creates a composite configuration from an XML based
44 * <em>configuration definition file</em>.
45 * </p>
46 * <p>
47 * This class provides an easy and flexible means for loading multiple
48 * configuration sources and combining the results into a single configuration
49 * object. The sources to be loaded are defined in an XML document that can
50 * contain certain tags representing the different supported configuration
51 * classes. If such a tag is found, the corresponding <code>Configuration</code>
52 * class is instantiated and initialized using the classes of the
53 * <code>beanutils</code> package (namely
54 * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code>
55 * will be used to extract the configuration's initialization parameters, which
56 * allows for complex initialization scenarios).
57 * </p>
58 * <p>
59 * It is also possible to add custom tags to the configuration definition file.
60 * For this purpose register your own <code>ConfigurationProvider</code>
61 * implementation for your tag using the <code>addConfigurationProvider()</code>
62 * method. This provider will then be called when the corresponding custom tag
63 * is detected. For the default configuration classes providers are already
64 * registered.
65 * </p>
66 * <p>
67 * The configuration definition file has the following basic structure:
68 * </p>
69 * <p>
70 *
71 * <pre>
72 * <configuration systemProperties="properties file name">
73 * <header>
74 * <!-- Optional meta information about the composite configuration -->
75 * </header>
76 * <override>
77 * <!-- Declarations for override configurations -->
78 * </override>
79 * <additional>
80 * <!-- Declarations for union configurations -->
81 * </additional>
82 * </configuration>
83 * </pre>
84 *
85 * </p>
86 * <p>
87 * The name of the root element (here <code>configuration</code>) is
88 * arbitrary. The optional systemProperties attribute identifies the path to
89 * a property file containing properties that should be added to the system
90 * properties. If specified on the root element, the system properties are
91 * set before the rest of the configuration is processed.
92 * </p>
93 * <p>
94 * There are two sections (both of them are optional) for declaring
95 * <em>override</em> and <em>additional</em> configurations. Configurations
96 * in the former section are evaluated in the order of their declaration, and
97 * properties of configurations declared earlier hide those of configurations
98 * declared later. Configurations in the latter section are combined to a union
99 * configuration, i.e. all of their properties are added to a large hierarchical
100 * configuration. Configuration declarations that occur as direct children of
101 * the root element are treated as override declarations.
102 * </p>
103 * <p>
104 * Each configuration declaration consists of a tag whose name is associated
105 * with a <code>ConfigurationProvider</code>. This can be one of the
106 * predefined tags like <code>properties</code>, or <code>xml</code>, or
107 * a custom tag, for which a configuration provider was registered. Attributes
108 * and sub elements with specific initialization parameters can be added. There
109 * are some reserved attributes with a special meaning that can be used in every
110 * configuration declaration:
111 * </p>
112 * <p>
113 * <table border="1">
114 * <tr>
115 * <th>Attribute</th>
116 * <th>Meaning</th>
117 * </tr>
118 * <tr>
119 * <td valign="top"><code>config-name</code></td>
120 * <td>Allows to specify a name for this configuration. This name can be used
121 * to obtain a reference to the configuration from the resulting combined
122 * configuration (see below).</td>
123 * </tr>
124 * <tr>
125 * <td valign="top"><code>config-at</code></td>
126 * <td>With this attribute an optional prefix can be specified for the
127 * properties of the corresponding configuration.</td>
128 * </tr>
129 * <tr>
130 * <td valign="top"><code>config-optional</code></td>
131 * <td>Declares a configuration as optional. This means that errors that occur
132 * when creating the configuration are ignored. (However
133 * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>s
134 * registered at the builder instance will get notified about this error: they
135 * receive an event of type <code>EVENT_ERR_LOAD_OPTIONAL</code>. The key
136 * property of this event contains the name of the optional configuration source
137 * that caused this problem.)</td>
138 * </tr>
139 * </table>
140 * </p>
141 * <p>
142 * The optional <em>header</em> section can contain some meta data about the
143 * created configuration itself. For instance, it is possible to set further
144 * properties of the <code>NodeCombiner</code> objects used for constructing
145 * the resulting configuration.
146 * </p>
147 * <p>
148 * The default configuration object returned by this builder is an instance of the
149 * <code>{@link CombinedConfiguration}</code> class. The return value of the
150 * <code>getConfiguration()</code> method can be casted to this type, and the
151 * <code>getConfiguration(boolean)</code> method directly declares
152 * <code>CombinedConfiguration</code> as return type. This allows for
153 * convenient access to the configuration objects maintained by the combined
154 * configuration (e.g. for updates of single configuration objects). It has also
155 * the advantage that the properties stored in all declared configuration
156 * objects are collected and transformed into a single hierarchical structure,
157 * which can be accessed using different expression engines. The actual CombinedConfiguration
158 * implementation can be overridden by specifying the class in the <em>config-class</em>
159 * attribute of the result element.
160 * </p>
161 * <p>
162 * Additional ConfigurationProviders can be added by configuring them in the <em>header</em>
163 * section.
164 * <pre>
165 * <providers>
166 * <provider config-tag="tag name" config-class="provider fully qualified class name"/>
167 * </providers>
168 * </pre>
169 * </p>
170 * <p>
171 * Additional variable resolvers can be added by configuring them in the <em>header</em>
172 * section.
173 * <pre>
174 * <lookups>
175 * <lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/>
176 * </lookups>
177 * </pre>
178 * </p>
179 * <p>
180 * All declared override configurations are directly added to the resulting
181 * combined configuration. If they are given names (using the
182 * <code>config-name</code> attribute), they can directly be accessed using
183 * the <code>getConfiguration(String)</code> method of
184 * <code>CombinedConfiguration</code>. The additional configurations are
185 * altogether added to another combined configuration, which uses a union
186 * combiner. Then this union configuration is added to the resulting combined
187 * configuration under the name defined by the <code>ADDITIONAL_NAME</code>
188 * constant.
189 * </p>
190 * <p>
191 * Implementation note: This class is not thread-safe. Especially the
192 * <code>getConfiguration()</code> methods should be called by a single thread
193 * only.
194 * </p>
195 *
196 * @since 1.3
197 * @author <a
198 * href="http://commons.apache.org/configuration/team-list.html">Commons
199 * Configuration team</a>
200 * @version $Id: DefaultConfigurationBuilder.java 727834 2008-12-18 22:16:32Z rgoers $
201 */
202 public class DefaultConfigurationBuilder extends XMLConfiguration implements
203 ConfigurationBuilder
204 {
205 /***
206 * Constant for the name of the additional configuration. If the
207 * configuration definition file contains an <code>additional</code>
208 * section, a special union configuration is created and added under this
209 * name to the resulting combined configuration.
210 */
211 public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class
212 .getName()
213 + "/ADDITIONAL_CONFIG";
214
215 /***
216 * Constant for the type of error events caused by optional configurations
217 * that cannot be loaded.
218 */
219 public static final int EVENT_ERR_LOAD_OPTIONAL = 51;
220
221 /*** Constant for the name of the configuration bean factory. */
222 static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class
223 .getName()
224 + ".CONFIG_BEAN_FACTORY_NAME";
225
226 /*** Constant for the reserved name attribute. */
227 static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
228 + XMLBeanDeclaration.RESERVED_PREFIX
229 + "name"
230 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
231
232 /*** Constant for the name of the at attribute. */
233 static final String ATTR_ATNAME = "at";
234
235 /*** Constant for the reserved at attribute. */
236 static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
237 + XMLBeanDeclaration.RESERVED_PREFIX
238 + ATTR_ATNAME
239 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
240
241 /*** Constant for the at attribute without the reserved prefix. */
242 static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
243 + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
244
245 /*** Constant for the name of the optional attribute. */
246 static final String ATTR_OPTIONALNAME = "optional";
247
248 /*** Constant for the reserved optional attribute. */
249 static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
250 + XMLBeanDeclaration.RESERVED_PREFIX
251 + ATTR_OPTIONALNAME
252 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
253
254 /*** Constant for the optional attribute without the reserved prefix. */
255 static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
256 + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
257
258 /*** Constant for the file name attribute. */
259 static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
260 + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
261
262 /*** Constant for the forceCreate attribute. */
263 static final String ATTR_FORCECREATE = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
264 + XMLBeanDeclaration.RESERVED_PREFIX
265 + "forceCreate"
266 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
267
268 /***
269 * Constant for the tag attribute for providers.
270 */
271 static final String KEY_SYSTEM_PROPS = "[@systemProperties]";
272
273 /*** Constant for the name of the header section. */
274 static final String SEC_HEADER = "header";
275
276 /*** Constant for an expression that selects the union configurations. */
277 static final String KEY_UNION = "additional";
278
279 /*** An array with the names of top level configuration sections.*/
280 static final String[] CONFIG_SECTIONS = {
281 "additional", "override", SEC_HEADER
282 };
283
284 /***
285 * Constant for an expression that selects override configurations in the
286 * override section.
287 */
288 static final String KEY_OVERRIDE = "override";
289
290 /***
291 * Constant for the key that points to the list nodes definition of the
292 * override combiner.
293 */
294 static final String KEY_OVERRIDE_LIST = SEC_HEADER
295 + ".combiner.override.list-nodes.node";
296
297 /***
298 * Constant for the key that points to the list nodes definition of the
299 * additional combiner.
300 */
301 static final String KEY_ADDITIONAL_LIST = SEC_HEADER
302 + ".combiner.additional.list-nodes.node";
303
304 /***
305 * Constant for the key for defining providers in the configuration file.
306 */
307 static final String KEY_CONFIGURATION_PROVIDERS = SEC_HEADER
308 + ".providers.provider";
309
310 /***
311 * Constant for the tag attribute for providers.
312 */
313 static final String KEY_PROVIDER_KEY = XMLBeanDeclaration.ATTR_PREFIX + "tag]";
314
315 /***
316 * Constant for the key for defining variable resolvers
317 */
318 static final String KEY_CONFIGURATION_LOOKUPS = SEC_HEADER
319 + ".lookups.lookup";
320
321 /***
322 * Constant for the prefix attribute for lookups.
323 */
324 static final String KEY_LOOKUP_KEY = XMLBeanDeclaration.ATTR_PREFIX + "prefix]";
325
326 /***
327 * Constant for the key of the result declaration. This key can point to a
328 * bean declaration, which defines properties of the resulting combined
329 * configuration.
330 */
331 static final String KEY_RESULT = SEC_HEADER + ".result";
332
333 /*** Constant for the key of the combiner in the result declaration.*/
334 static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
335
336 /*** Constant for the XML file extension. */
337 static final String EXT_XML = ".xml";
338
339 /*** Constant for the provider for properties files. */
340 private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(
341 XMLPropertiesConfiguration.class, PropertiesConfiguration.class,
342 EXT_XML);
343
344 /*** Constant for the provider for XML files. */
345 private static final ConfigurationProvider XML_PROVIDER = new XMLConfigurationProvider();
346
347 /*** Constant for the provider for JNDI sources. */
348 private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(
349 JNDIConfiguration.class);
350
351 /*** Constant for the provider for system properties. */
352 private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(
353 SystemConfiguration.class);
354
355 /*** Constant for the provider for plist files. */
356 private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(
357 "org.apache.commons.configuration.plist.XMLPropertyListConfiguration",
358 "org.apache.commons.configuration.plist.PropertyListConfiguration",
359 EXT_XML);
360
361 /*** Constant for the provider for configuration definition files.*/
362 private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();
363
364 /*** An array with the names of the default tags. */
365 private static final String[] DEFAULT_TAGS =
366 {"properties", "xml", "hierarchicalXml", "jndi", "system", "plist", "configuration"};
367
368 /*** An array with the providers for the default tags. */
369 private static final ConfigurationProvider[] DEFAULT_PROVIDERS =
370 {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,
371 SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER};
372
373 /***
374 * The serial version UID.
375 */
376 private static final long serialVersionUID = -3113777854714492123L;
377
378 /*** Stores the configuration that is currently constructed.*/
379 private CombinedConfiguration constructedConfiguration;
380
381 /*** Stores a map with the registered configuration providers. */
382 private Map providers;
383
384 /*** Stores the base path to the configuration sources to load. */
385 private String configurationBasePath;
386
387 /***
388 * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A
389 * configuration definition file is not yet loaded. Use the diverse setter
390 * methods provided by file based configurations to specify the
391 * configuration definition file.
392 */
393 public DefaultConfigurationBuilder()
394 {
395 super();
396 providers = new HashMap();
397 registerDefaultProviders();
398 registerBeanFactory();
399 setLogger(LogFactory.getLog(getClass()));
400 addErrorLogListener();
401 }
402
403 /***
404 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
405 * sets the specified configuration definition file.
406 *
407 * @param file the configuration definition file
408 */
409 public DefaultConfigurationBuilder(File file)
410 {
411 this();
412 setFile(file);
413 }
414
415 /***
416 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
417 * sets the specified configuration definition file.
418 *
419 * @param fileName the name of the configuration definition file
420 * @throws ConfigurationException if an error occurs when the file is loaded
421 */
422 public DefaultConfigurationBuilder(String fileName)
423 throws ConfigurationException
424 {
425 this();
426 setFileName(fileName);
427 }
428
429 /***
430 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
431 * sets the specified configuration definition file.
432 *
433 * @param url the URL to the configuration definition file
434 * @throws ConfigurationException if an error occurs when the file is loaded
435 */
436 public DefaultConfigurationBuilder(URL url) throws ConfigurationException
437 {
438 this();
439 setURL(url);
440 }
441
442 /***
443 * Returns the base path for the configuration sources to load. This path is
444 * used to resolve relative paths in the configuration definition file.
445 *
446 * @return the base path for configuration sources
447 */
448 public String getConfigurationBasePath()
449 {
450 return (configurationBasePath != null) ? configurationBasePath
451 : getBasePath();
452 }
453
454 /***
455 * Sets the base path for the configuration sources to load. Normally a base
456 * path need not to be set because it is determined by the location of the
457 * configuration definition file to load. All relative paths in this file
458 * are resolved relative to this file. Setting a base path makes sense if
459 * such relative paths should be otherwise resolved, e.g. if the
460 * configuration file is loaded from the class path and all sub
461 * configurations it refers to are stored in a special config directory.
462 *
463 * @param configurationBasePath the new base path to set
464 */
465 public void setConfigurationBasePath(String configurationBasePath)
466 {
467 this.configurationBasePath = configurationBasePath;
468 }
469
470 /***
471 * Adds a configuration provider for the specified tag. Whenever this tag is
472 * encountered in the configuration definition file this provider will be
473 * called to create the configuration object.
474 *
475 * @param tagName the name of the tag in the configuration definition file
476 * @param provider the provider for this tag
477 */
478 public void addConfigurationProvider(String tagName,
479 ConfigurationProvider provider)
480 {
481 if (tagName == null)
482 {
483 throw new IllegalArgumentException("Tag name must not be null!");
484 }
485 if (provider == null)
486 {
487 throw new IllegalArgumentException("Provider must not be null!");
488 }
489
490 providers.put(tagName, provider);
491 }
492
493 /***
494 * Removes the configuration provider for the specified tag name.
495 *
496 * @param tagName the tag name
497 * @return the removed configuration provider or <b>null</b> if none was
498 * registered for that tag
499 */
500 public ConfigurationProvider removeConfigurationProvider(String tagName)
501 {
502 return (ConfigurationProvider) providers.remove(tagName);
503 }
504
505 /***
506 * Returns the configuration provider for the given tag.
507 *
508 * @param tagName the name of the tag
509 * @return the provider that was registered for this tag or <b>null</b> if
510 * there is none
511 */
512 public ConfigurationProvider providerForTag(String tagName)
513 {
514 return (ConfigurationProvider) providers.get(tagName);
515 }
516
517 /***
518 * Returns the configuration provided by this builder. Loads and parses the
519 * configuration definition file and creates instances for the declared
520 * configurations.
521 *
522 * @return the configuration
523 * @throws ConfigurationException if an error occurs
524 */
525 public Configuration getConfiguration() throws ConfigurationException
526 {
527 return getConfiguration(true);
528 }
529
530 /***
531 * Returns the configuration provided by this builder. If the boolean
532 * parameter is <b>true</b>, the configuration definition file will be
533 * loaded. It will then be parsed, and instances for the declared
534 * configurations will be created.
535 *
536 * @param load a flag whether the configuration definition file should be
537 * loaded; a value of <b>false</b> would make sense if the file has already
538 * been created or its content was manipulated using some of the property
539 * accessor methods
540 * @return the configuration
541 * @throws ConfigurationException if an error occurs
542 */
543 public CombinedConfiguration getConfiguration(boolean load)
544 throws ConfigurationException
545 {
546 if (load)
547 {
548 load();
549 }
550
551 initSystemProperties();
552 registerConfiguredProviders();
553 registerConfiguredLookups();
554
555 CombinedConfiguration result = createResultConfiguration();
556 constructedConfiguration = result;
557
558 List overrides = fetchTopLevelOverrideConfigs();
559 overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
560 initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
561
562 List additionals = fetchChildConfigs(KEY_UNION);
563 if (!additionals.isEmpty())
564 {
565 CombinedConfiguration addConfig = new CombinedConfiguration(
566 new UnionCombiner());
567 result.addConfiguration(addConfig, ADDITIONAL_NAME);
568 initCombinedConfiguration(addConfig, additionals,
569 KEY_ADDITIONAL_LIST);
570 }
571
572 return result;
573 }
574
575 /***
576 * Creates the resulting combined configuration. This method is called by
577 * <code>getConfiguration()</code>. It checks whether the
578 * <code>header</code> section of the configuration definition file
579 * contains a <code>result</code> element. If this is the case, it will be
580 * used to initialize the properties of the newly created configuration
581 * object.
582 *
583 * @return the resulting configuration object
584 * @throws ConfigurationException if an error occurs
585 */
586 protected CombinedConfiguration createResultConfiguration()
587 throws ConfigurationException
588 {
589 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
590 CombinedConfiguration result = (CombinedConfiguration) BeanHelper
591 .createBean(decl, CombinedConfiguration.class);
592
593 if (getMaxIndex(KEY_COMBINER) < 0)
594 {
595
596 result.setNodeCombiner(new OverrideCombiner());
597 }
598
599 return result;
600 }
601
602 /***
603 * Initializes a combined configuration for the configurations of a specific
604 * section. This method is called for the override and for the additional
605 * section (if it exists).
606 *
607 * @param config the configuration to be initialized
608 * @param containedConfigs the list with the declaratinos of the contained
609 * configurations
610 * @param keyListNodes a list with the declaration of list nodes
611 * @throws ConfigurationException if an error occurs
612 */
613 protected void initCombinedConfiguration(CombinedConfiguration config,
614 List containedConfigs, String keyListNodes) throws ConfigurationException
615 {
616 List listNodes = getList(keyListNodes);
617 for (Iterator it = listNodes.iterator(); it.hasNext();)
618 {
619 config.getNodeCombiner().addListNode((String) it.next());
620 }
621
622 for (Iterator it = containedConfigs.iterator(); it.hasNext();)
623 {
624 HierarchicalConfiguration conf = (HierarchicalConfiguration) it
625 .next();
626 ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
627 conf);
628 AbstractConfiguration newConf = createConfigurationAt(decl);
629 if (newConf != null)
630 {
631 config.addConfiguration(newConf, decl.getConfiguration()
632 .getString(ATTR_NAME), decl.getAt());
633 }
634 }
635 }
636
637 /***
638 * Registers the default configuration providers supported by this class.
639 * This method will be called during initialization. It registers
640 * configuration providers for the tags that are supported by default.
641 */
642 protected void registerDefaultProviders()
643 {
644 for (int i = 0; i < DEFAULT_TAGS.length; i++)
645 {
646 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
647 }
648 }
649
650 /***
651 * Registers providers defined in the configuration.
652 *
653 * @throws ConfigurationException if an error occurs
654 */
655 protected void registerConfiguredProviders() throws ConfigurationException
656 {
657 List nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS);
658 for (Iterator it = nodes.iterator(); it.hasNext();)
659 {
660 HierarchicalConfiguration config = (HierarchicalConfiguration) it.next();
661 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
662 String key = config.getString(KEY_PROVIDER_KEY);
663 addConfigurationProvider(key, (ConfigurationProvider) BeanHelper
664 .createBean(decl));
665 }
666 }
667
668 /***
669 * Registers StrLookups defined in the configuration.
670 *
671 * @throws ConfigurationException if an error occurs
672 */
673 protected void registerConfiguredLookups() throws ConfigurationException
674 {
675 List nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS);
676 for (Iterator it = nodes.iterator(); it.hasNext();)
677 {
678 HierarchicalConfiguration config = (HierarchicalConfiguration) it.next();
679 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
680 String key = config.getString(KEY_LOOKUP_KEY);
681 ConfigurationInterpolator.registerGlobalLookup(key, (StrLookup) BeanHelper.createBean(decl));
682 }
683 }
684
685 /***
686 * If a property file is configured add the properties to the System properties.
687 * @throws ConfigurationException if an error occurs.
688 */
689 protected void initSystemProperties() throws ConfigurationException
690 {
691 String fileName = getString(KEY_SYSTEM_PROPS);
692 if (fileName != null)
693 {
694 try
695 {
696 SystemConfiguration.setSystemProperties(fileName);
697 }
698 catch (Exception ex)
699 {
700 throw new ConfigurationException("Error setting system properties from " + fileName, ex);
701 }
702
703 }
704 }
705
706 /***
707 * Performs interpolation. This method will not only take this configuration
708 * instance into account (which is the one that loaded the configuration
709 * definition file), but also the so far constructed combined configuration.
710 * So variables can be used that point to properties that are defined in
711 * configuration sources loaded by this builder.
712 *
713 * @param value the value to be interpolated
714 * @return the interpolated value
715 */
716 protected Object interpolate(Object value)
717 {
718 Object result = super.interpolate(value);
719 if (constructedConfiguration != null)
720 {
721 result = constructedConfiguration.interpolate(result);
722 }
723 return result;
724 }
725
726 protected void fireError(int type, String propName, Object propValue,
727 Throwable ex)
728 {
729
730
731 super.fireError(type, propName, propValue, ex);
732 }
733
734 /***
735 * Creates a configuration object from the specified configuration
736 * declaration.
737 *
738 * @param decl the configuration declaration
739 * @return the new configuration object
740 * @throws ConfigurationException if an error occurs
741 */
742 private AbstractConfiguration createConfigurationAt(
743 ConfigurationDeclaration decl) throws ConfigurationException
744 {
745 try
746 {
747 return (AbstractConfiguration) BeanHelper.createBean(decl);
748 }
749 catch (Exception ex)
750 {
751
752 throw new ConfigurationException(ex);
753 }
754 }
755
756 /***
757 * Returns a list with <code>SubnodeConfiguration</code> objects for the
758 * child nodes of the specified configuration node.
759 *
760 * @param node the start node
761 * @return a list with subnode configurations for the node's children
762 */
763 private List fetchChildConfigs(ConfigurationNode node)
764 {
765 List children = node.getChildren();
766 List result = new ArrayList(children.size());
767 for (Iterator it = children.iterator(); it.hasNext();)
768 {
769 result.add(createSubnodeConfiguration((Node) it.next()));
770 }
771 return result;
772 }
773
774 /***
775 * Returns a list with <code>SubnodeConfiguration</code> objects for the
776 * child nodes of the node specified by the given key.
777 *
778 * @param key the key (must define exactly one node)
779 * @return a list with subnode configurations for the node's children
780 */
781 private List fetchChildConfigs(String key)
782 {
783 List nodes = fetchNodeList(key);
784 if (nodes.size() > 0)
785 {
786 return fetchChildConfigs((ConfigurationNode) nodes.get(0));
787 }
788 else
789 {
790 return Collections.EMPTY_LIST;
791 }
792 }
793
794 /***
795 * Finds the override configurations that are defined as top level elements
796 * in the configuration definition file. This method will fetch the child
797 * elements of the root node and remove the nodes that represent other
798 * configuration sections. The remaining nodes are treated as definitions
799 * for override configurations.
800 *
801 * @return a list with subnode configurations for the top level override
802 * configurations
803 */
804 private List fetchTopLevelOverrideConfigs()
805 {
806 List configs = fetchChildConfigs(getRootNode());
807 for (Iterator it = configs.iterator(); it.hasNext();)
808 {
809 String nodeName = ((SubnodeConfiguration) it.next()).getRootNode()
810 .getName();
811 for (int i = 0; i < CONFIG_SECTIONS.length; i++)
812 {
813 if (CONFIG_SECTIONS[i].equals(nodeName))
814 {
815 it.remove();
816 break;
817 }
818 }
819 }
820 return configs;
821 }
822
823 /***
824 * Registers the bean factory used by this class if necessary. This method
825 * is called by the constructor to ensure that the required bean factory is
826 * available.
827 */
828 private void registerBeanFactory()
829 {
830 synchronized (DefaultConfigurationBuilder.class)
831 {
832 if (!BeanHelper.registeredFactoryNames().contains(
833 CONFIG_BEAN_FACTORY_NAME))
834 {
835 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
836 new ConfigurationBeanFactory());
837 }
838 }
839 }
840
841 /***
842 * <p>
843 * A base class for creating and initializing configuration sources.
844 * </p>
845 * <p>
846 * Concrete sub classes of this base class are responsible for creating
847 * specific <code>Configuration</code> objects for the tags in the
848 * configuration definition file. The configuration factory will parse the
849 * definition file and try to find a matching
850 * <code>ConfigurationProvider</code> for each encountered tag. This
851 * provider is then asked to create a corresponding
852 * <code>Configuration</code> object. It is up to a concrete
853 * implementation how this object is created and initialized.
854 * </p>
855 * <p>
856 * Note that at the moment only configuration classes derived from
857 * <code>{@link AbstractConfiguration}</code> are supported.
858 * </p>
859 */
860 public static class ConfigurationProvider extends DefaultBeanFactory
861 {
862 /*** Stores the class of the configuration to be created. */
863 private Class configurationClass;
864
865 /*** Stores the name of the configuration class to be created.*/
866 private String configurationClassName;
867
868 /***
869 * Creates a new uninitialized instance of
870 * <code>ConfigurationProvider</code>.
871 */
872 public ConfigurationProvider()
873 {
874 this((Class) null);
875 }
876
877 /***
878 * Creates a new instance of <code>ConfigurationProvider</code> and
879 * sets the class of the configuration created by this provider.
880 *
881 * @param configClass the configuration class
882 */
883 public ConfigurationProvider(Class configClass)
884 {
885 setConfigurationClass(configClass);
886 }
887
888 /***
889 * Creates a new instance of <code>ConfigurationProvider</code> and
890 * sets the name of the class of the configuration created by this
891 * provider.
892 *
893 * @param configClassName the name of the configuration class
894 * @since 1.4
895 */
896 public ConfigurationProvider(String configClassName)
897 {
898 setConfigurationClassName(configClassName);
899 }
900
901 /***
902 * Returns the class of the configuration returned by this provider.
903 *
904 * @return the class of the provided configuration
905 */
906 public Class getConfigurationClass()
907 {
908 return configurationClass;
909 }
910
911 /***
912 * Sets the class of the configuration returned by this provider.
913 *
914 * @param configurationClass the configuration class
915 */
916 public void setConfigurationClass(Class configurationClass)
917 {
918 this.configurationClass = configurationClass;
919 }
920
921 /***
922 * Returns the name of the configuration class returned by this
923 * provider.
924 *
925 * @return the configuration class name
926 * @since 1.4
927 */
928 public String getConfigurationClassName()
929 {
930 return configurationClassName;
931 }
932
933 /***
934 * Sets the name of the configuration class returned by this provider.
935 *
936 * @param configurationClassName the name of the configuration class
937 * @since 1.4
938 */
939 public void setConfigurationClassName(String configurationClassName)
940 {
941 this.configurationClassName = configurationClassName;
942 }
943
944 /***
945 * Returns the configuration. This method is called to fetch the
946 * configuration from the provider. This implementation will call the
947 * inherited <code>{@link
948 * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
949 * createBean()}</code> method to create a new instance of the
950 * configuration class.
951 *
952 * @param decl the bean declaration with initialization parameters for
953 * the configuration
954 * @return the new configuration object
955 * @throws Exception if an error occurs
956 */
957 public AbstractConfiguration getConfiguration(
958 ConfigurationDeclaration decl) throws Exception
959 {
960 return (AbstractConfiguration) createBean(fetchConfigurationClass(),
961 decl, null);
962 }
963
964 /***
965 * Returns an uninitialized configuration of the represented type. This
966 * method will be called for optional configurations when the
967 * <code>getConfiguration()</code> method caused an error and the
968 * <code>forceCreate</code> attribute is set. A concrete sub class can
969 * here try to create an uninitialized, empty configuration, which may
970 * be possible if the error was created during initialization. This base
971 * implementation just returns <b>null</b>.
972 *
973 * @param decl the bean declaration with initialization parameters for
974 * the configuration
975 * @return the new configuration object
976 * @throws Exception if an error occurs
977 * @since 1.4
978 */
979 public AbstractConfiguration getEmptyConfiguration(
980 ConfigurationDeclaration decl) throws Exception
981 {
982 return null;
983 }
984
985 /***
986 * Returns the configuration class supported by this provider. If a
987 * class object was set, it is returned. Otherwise the method tries to
988 * resolve the class name.
989 *
990 * @return the class of the configuration to be created
991 * @since 1.4
992 */
993 protected synchronized Class fetchConfigurationClass() throws Exception
994 {
995 if (getConfigurationClass() == null)
996 {
997 setConfigurationClass(loadClass(getConfigurationClassName()));
998 }
999 return getConfigurationClass();
1000 }
1001
1002 /***
1003 * Loads the class with the specified name dynamically. If the class's
1004 * name is <b>null</b>, <b>null</b> will also be returned.
1005 *
1006 * @param className the name of the class to be loaded
1007 * @return the class object
1008 * @throws ClassNotFoundException if class loading fails
1009 * @since 1.4
1010 */
1011 protected Class loadClass(String className)
1012 throws ClassNotFoundException
1013 {
1014 return (className != null) ? Class.forName(className, true,
1015 getClass().getClassLoader()) : null;
1016 }
1017 }
1018
1019 /***
1020 * <p>
1021 * A specialized <code>BeanDeclaration</code> implementation that
1022 * represents the declaration of a configuration source.
1023 * </p>
1024 * <p>
1025 * Instances of this class are able to extract all information about a
1026 * configuration source from the configuration definition file. The
1027 * declaration of a configuration source is very similar to a bean
1028 * declaration processed by <code>XMLBeanDeclaration</code>. There are
1029 * very few differences, e.g. some reserved attributes like
1030 * <code>optional</code> and <code>at</code> and the fact that a bean
1031 * factory is never needed.
1032 * </p>
1033 */
1034 public static class ConfigurationDeclaration extends XMLBeanDeclaration
1035 {
1036 /*** Stores a reference to the associated configuration builder. */
1037 private DefaultConfigurationBuilder configurationBuilder;
1038
1039 /***
1040 * Creates a new instance of <code>ConfigurationDeclaration</code> and
1041 * initializes it.
1042 *
1043 * @param builder the associated configuration builder
1044 * @param config the configuration this declaration is based onto
1045 */
1046 public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
1047 HierarchicalConfiguration config)
1048 {
1049 super(config);
1050 configurationBuilder = builder;
1051 }
1052
1053 /***
1054 * Returns the associated configuration builder.
1055 *
1056 * @return the configuration builder
1057 */
1058 public DefaultConfigurationBuilder getConfigurationBuilder()
1059 {
1060 return configurationBuilder;
1061 }
1062
1063 /***
1064 * Returns the value of the <code>at</code> attribute.
1065 *
1066 * @return the value of the <code>at</code> attribute (can be <b>null</b>)
1067 */
1068 public String getAt()
1069 {
1070 String result = this.getConfiguration().getString(ATTR_AT_RES);
1071 return (result == null) ? this.getConfiguration().getString(ATTR_AT)
1072 : result;
1073 }
1074
1075 /***
1076 * Returns a flag whether this is an optional configuration.
1077 *
1078 * @return a flag if this declaration points to an optional
1079 * configuration
1080 */
1081 public boolean isOptional()
1082 {
1083 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
1084 null);
1085 if (value == null)
1086 {
1087 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
1088 Boolean.FALSE);
1089 }
1090 return value.booleanValue();
1091 }
1092
1093 /***
1094 * Returns a flag whether this configuration should always be created
1095 * and added to the resulting combined configuration. This flag is
1096 * evaluated only for optional configurations whose normal creation has
1097 * caused an error. If for such a configuration the
1098 * <code>forceCreate</code> attribute is set and the corresponding
1099 * configuration provider supports this mode, an empty configuration
1100 * will be created and added to the resulting combined configuration.
1101 *
1102 * @return the value of the <code>forceCreate</code> attribute
1103 * @since 1.4
1104 */
1105 public boolean isForceCreate()
1106 {
1107 return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
1108 }
1109
1110 /***
1111 * Returns the name of the bean factory. For configuration source
1112 * declarations always a reserved factory is used. This factory's name
1113 * is returned by this implementation.
1114 *
1115 * @return the name of the bean factory
1116 */
1117 public String getBeanFactoryName()
1118 {
1119 return CONFIG_BEAN_FACTORY_NAME;
1120 }
1121
1122 /***
1123 * Returns the bean's class name. This implementation will always return
1124 * <b>null</b>.
1125 *
1126 * @return the name of the bean's class
1127 */
1128 public String getBeanClassName()
1129 {
1130 return null;
1131 }
1132
1133 /***
1134 * Checks whether the given node is reserved. This method will take
1135 * further reserved attributes into account
1136 *
1137 * @param nd the node
1138 * @return a flag whether this node is reserved
1139 */
1140 protected boolean isReservedNode(ConfigurationNode nd)
1141 {
1142 if (super.isReservedNode(nd))
1143 {
1144 return true;
1145 }
1146
1147 return nd.isAttribute()
1148 && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
1149 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
1150 .equals(nd.getName()) && nd.getParentNode()
1151 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
1152 }
1153
1154 /***
1155 * Performs interpolation. This implementation will delegate
1156 * interpolation to the configuration builder, which takes care that the
1157 * currently constructed configuration is taken into account, too.
1158 *
1159 * @param value the value to be interpolated
1160 * @return the interpolated value
1161 */
1162 protected Object interpolate(Object value)
1163 {
1164 return getConfigurationBuilder().interpolate(value);
1165 }
1166 }
1167
1168 /***
1169 * A specialized <code>BeanFactory</code> implementation that handles
1170 * configuration declarations. This class will retrieve the correct
1171 * configuration provider and delegate the task of creating the
1172 * configuration to this object.
1173 */
1174 static class ConfigurationBeanFactory implements BeanFactory
1175 {
1176 /***
1177 * Creates an instance of a bean class. This implementation expects that
1178 * the passed in bean declaration is a declaration for a configuration.
1179 * It will determine the responsible configuration provider and delegate
1180 * the call to this instance. If creation of the configuration fails
1181 * and the <code>optional</code> attribute is set, the exception will
1182 * be ignored. If the <code>forceCreate</code> attribute is set, too,
1183 * the provider is asked to create an empty configuration. A return
1184 * value of <b>null</b> means that no configuration could be created.
1185 *
1186 * @param beanClass the bean class (will be ignored)
1187 * @param data the declaration
1188 * @param param an additional parameter (will be ignored)
1189 * @return the newly created configuration
1190 * @throws Exception if an error occurs
1191 */
1192 public Object createBean(Class beanClass, BeanDeclaration data,
1193 Object param) throws Exception
1194 {
1195 ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1196 String tagName = decl.getNode().getName();
1197 ConfigurationProvider provider = decl.getConfigurationBuilder()
1198 .providerForTag(tagName);
1199 if (provider == null)
1200 {
1201 throw new ConfigurationRuntimeException(
1202 "No ConfigurationProvider registered for tag "
1203 + tagName);
1204 }
1205
1206 try
1207 {
1208 return provider.getConfiguration(decl);
1209 }
1210 catch (Exception ex)
1211 {
1212
1213 if (!decl.isOptional())
1214 {
1215 throw ex;
1216 }
1217 else
1218 {
1219
1220 decl.getConfigurationBuilder().fireError(
1221 EVENT_ERR_LOAD_OPTIONAL,
1222 decl.getConfiguration().getString(ATTR_NAME), null,
1223 ex);
1224
1225 if (decl.isForceCreate())
1226 {
1227 try
1228 {
1229 return provider.getEmptyConfiguration(decl);
1230 }
1231 catch (Exception ex2)
1232 {
1233
1234 ;
1235 }
1236 }
1237 return null;
1238 }
1239 }
1240 }
1241
1242 /***
1243 * Returns the default class for this bean factory.
1244 *
1245 * @return the default class
1246 */
1247 public Class getDefaultBeanClass()
1248 {
1249
1250
1251 return Configuration.class;
1252 }
1253 }
1254
1255 /***
1256 * A specialized provider implementation that deals with file based
1257 * configurations. Ensures that the base path is correctly set and that the
1258 * load() method gets called.
1259 */
1260 public static class FileConfigurationProvider extends ConfigurationProvider
1261 {
1262 /***
1263 * Creates a new instance of <code>FileConfigurationProvider</code>.
1264 */
1265 public FileConfigurationProvider()
1266 {
1267 super();
1268 }
1269
1270 /***
1271 * Creates a new instance of <code>FileConfigurationProvider</code>
1272 * and sets the configuration class.
1273 *
1274 * @param configClass the class for the configurations to be created
1275 */
1276 public FileConfigurationProvider(Class configClass)
1277 {
1278 super(configClass);
1279 }
1280
1281 /***
1282 * Creates a new instance of <code>FileConfigurationProvider</code>
1283 * and sets the configuration class name.
1284 *
1285 * @param configClassName the name of the configuration to be created
1286 * @since 1.4
1287 */
1288 public FileConfigurationProvider(String configClassName)
1289 {
1290 super(configClassName);
1291 }
1292
1293 /***
1294 * Creates the configuration. After that <code>load()</code> will be
1295 * called. If this configuration is marked as optional, exceptions will
1296 * be ignored.
1297 *
1298 * @param decl the declaration
1299 * @return the new configuration
1300 * @throws Exception if an error occurs
1301 */
1302 public AbstractConfiguration getConfiguration(
1303 ConfigurationDeclaration decl) throws Exception
1304 {
1305 AbstractConfiguration result = getEmptyConfiguration(decl);
1306 ((FileConfiguration) result).load();
1307 return result;
1308 }
1309
1310 /***
1311 * Returns an uninitialized file configuration. This method will be
1312 * called for optional configurations when the
1313 * <code>getConfiguration()</code> method caused an error and the
1314 * <code>forceCreate</code> attribute is set. It will create the
1315 * configuration of the represented type, but the <code>load()</code>
1316 * method won't be called. This way non-existing configuration files can
1317 * be handled gracefully: If loading a the file fails, an empty
1318 * configuration will be created that is already configured with the
1319 * correct file name.
1320 *
1321 * @param decl the bean declaration with initialization parameters for
1322 * the configuration
1323 * @return the new configuration object
1324 * @throws Exception if an error occurs
1325 * @since 1.4
1326 */
1327 public AbstractConfiguration getEmptyConfiguration(
1328 ConfigurationDeclaration decl) throws Exception
1329 {
1330 return super.getConfiguration(decl);
1331 }
1332
1333 /***
1334 * Initializes the bean instance. Ensures that the file configuration's
1335 * base path will be initialized with the base path of the factory so
1336 * that relative path names can be correctly resolved.
1337 *
1338 * @param bean the bean to be initialized
1339 * @param data the declaration
1340 * @throws Exception if an error occurs
1341 */
1342 protected void initBeanInstance(Object bean, BeanDeclaration data)
1343 throws Exception
1344 {
1345 FileConfiguration config = (FileConfiguration) bean;
1346 config.setBasePath(((ConfigurationDeclaration) data)
1347 .getConfigurationBuilder().getConfigurationBasePath());
1348 super.initBeanInstance(bean, data);
1349 }
1350 }
1351
1352 /***
1353 * A specialized configuration provider for XML configurations. This
1354 * implementation acts like a <code>FileConfigurationProvider</code>, but
1355 * it will copy all entity IDs that have been registered for the
1356 * configuration builder to the new XML configuration before it is loaded.
1357 *
1358 * @since 1.6
1359 */
1360 public static class XMLConfigurationProvider extends FileConfigurationProvider
1361 {
1362 /***
1363 * Creates a new instance of <code>XMLConfigurationProvider</code>.
1364 */
1365 public XMLConfigurationProvider()
1366 {
1367 super(XMLConfiguration.class);
1368 }
1369
1370 /***
1371 * Returns a new empty configuration instance. This implementation
1372 * performs some additional initialization specific to XML
1373 * configurations.
1374 *
1375 * @param decl the configuration declaration
1376 * @return the new configuration
1377 * @throws Exception if an error occurs
1378 */
1379 public AbstractConfiguration getEmptyConfiguration(
1380 ConfigurationDeclaration decl) throws Exception
1381 {
1382 XMLConfiguration config = (XMLConfiguration) super
1383 .getEmptyConfiguration(decl);
1384
1385
1386 DefaultConfigurationBuilder builder = decl
1387 .getConfigurationBuilder();
1388 config.getRegisteredEntities().putAll(
1389 builder.getRegisteredEntities());
1390 return config;
1391 }
1392 }
1393
1394 /***
1395 * A specialized configuration provider for file based configurations that
1396 * can handle configuration sources whose concrete type depends on the
1397 * extension of the file to be loaded. One example is the
1398 * <code>properties</code> tag: if the file ends with ".xml" a
1399 * XMLPropertiesConfiguration object must be created, otherwise a
1400 * PropertiesConfiguration object.
1401 */
1402 static class FileExtensionConfigurationProvider extends
1403 FileConfigurationProvider
1404 {
1405 /***
1406 * Stores the class to be created when the file extension matches.
1407 */
1408 private Class matchingClass;
1409
1410 /***
1411 * Stores the name of the class to be created when the file extension
1412 * matches.
1413 */
1414 private String matchingClassName;
1415
1416 /***
1417 * Stores the class to be created when the file extension does not
1418 * match.
1419 */
1420 private Class defaultClass;
1421
1422 /***
1423 * Stores the name of the class to be created when the file extension
1424 * does not match.
1425 */
1426 private String defaultClassName;
1427
1428 /*** Stores the file extension to be checked against. */
1429 private String fileExtension;
1430
1431 /***
1432 * Creates a new instance of
1433 * <code>FileExtensionConfigurationProvider</code> and initializes it.
1434 *
1435 * @param matchingClass the class to be created when the file extension
1436 * matches
1437 * @param defaultClass the class to be created when the file extension
1438 * does not match
1439 * @param extension the file extension to be checked agains
1440 */
1441 public FileExtensionConfigurationProvider(Class matchingClass,
1442 Class defaultClass, String extension)
1443 {
1444 this.matchingClass = matchingClass;
1445 this.defaultClass = defaultClass;
1446 fileExtension = extension;
1447 }
1448
1449 /***
1450 * Creates a new instance of
1451 * <code>FileExtensionConfigurationProvider</code> and initializes it
1452 * with the names of the classes to be created.
1453 *
1454 * @param matchingClassName the name of the class to be created when the
1455 * file extension matches
1456 * @param defaultClassName the name of the class to be created when the
1457 * file extension does not match
1458 * @param extension the file extension to be checked against
1459 * @since 1.4
1460 */
1461 public FileExtensionConfigurationProvider(String matchingClassName,
1462 String defaultClassName, String extension)
1463 {
1464 this.matchingClassName = matchingClassName;
1465 this.defaultClassName = defaultClassName;
1466 fileExtension = extension;
1467 }
1468
1469 /***
1470 * Returns the matching class object, no matter whether it was defined
1471 * as a class or as a class name.
1472 *
1473 * @return the matching class object
1474 * @throws Exception if an error occurs
1475 * @since 1.4
1476 */
1477 protected synchronized Class fetchMatchingClass() throws Exception
1478 {
1479 if (matchingClass == null)
1480 {
1481 matchingClass = loadClass(matchingClassName);
1482 }
1483 return matchingClass;
1484 }
1485
1486 /***
1487 * Returns the default class object, no matter whether it was defined as
1488 * a class or as a class name.
1489 *
1490 * @return the default class object
1491 * @throws Exception if an error occurs
1492 * @since 1.4
1493 */
1494 protected synchronized Class fetchDefaultClass() throws Exception
1495 {
1496 if (defaultClass == null)
1497 {
1498 defaultClass = loadClass(defaultClassName);
1499 }
1500 return defaultClass;
1501 }
1502
1503 /***
1504 * Creates the configuration object. The class is determined by the file
1505 * name's extension.
1506 *
1507 * @param beanClass the class
1508 * @param data the bean declaration
1509 * @return the new bean
1510 * @throws Exception if an error occurs
1511 */
1512 protected Object createBeanInstance(Class beanClass,
1513 BeanDeclaration data) throws Exception
1514 {
1515 String fileName = ((ConfigurationDeclaration) data)
1516 .getConfiguration().getString(ATTR_FILENAME);
1517 if (fileName != null
1518 && fileName.toLowerCase().trim().endsWith(fileExtension))
1519 {
1520 return super.createBeanInstance(fetchMatchingClass(), data);
1521 }
1522 else
1523 {
1524 return super.createBeanInstance(fetchDefaultClass(), data);
1525 }
1526 }
1527 }
1528
1529 /***
1530 * A specialized configuration provider class that allows to include other
1531 * configuration definition files.
1532 */
1533 static class ConfigurationBuilderProvider extends ConfigurationProvider
1534 {
1535 /***
1536 * Creates a new instance of <code>ConfigurationBuilderProvider</code>.
1537 */
1538 public ConfigurationBuilderProvider()
1539 {
1540 super(DefaultConfigurationBuilder.class);
1541 }
1542
1543 /***
1544 * Creates the configuration. First creates a configuration builder
1545 * object. Then returns the configuration created by this builder.
1546 *
1547 * @param decl the configuration declaration
1548 * @return the configuration
1549 * @exception Exception if an error occurs
1550 */
1551 public AbstractConfiguration getConfiguration(
1552 ConfigurationDeclaration decl) throws Exception
1553 {
1554 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
1555 .getConfiguration(decl);
1556 return builder.getConfiguration(true);
1557 }
1558
1559 /***
1560 * Returns an empty configuration in case of an optional configuration
1561 * could not be created. This implementation returns an empty combined
1562 * configuration.
1563 *
1564 * @param decl the configuration declaration
1565 * @return the configuration
1566 * @exception Exception if an error occurs
1567 * @since 1.4
1568 */
1569 public AbstractConfiguration getEmptyConfiguration(
1570 ConfigurationDeclaration decl) throws Exception
1571 {
1572 return new CombinedConfiguration();
1573 }
1574 }
1575 }