1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
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.tree.ConfigurationNode;
34 import org.apache.commons.configuration.tree.DefaultExpressionEngine;
35 import org.apache.commons.configuration.tree.OverrideCombiner;
36 import org.apache.commons.configuration.tree.UnionCombiner;
37 import org.apache.commons.logging.LogFactory;
38
39 /**
40 * <p>
41 * A factory class that creates a composite configuration from an XML based
42 * <em>configuration definition file</em>.
43 * </p>
44 * <p>
45 * This class provides an easy and flexible means for loading multiple
46 * configuration sources and combining the results into a single configuration
47 * object. The sources to be loaded are defined in an XML document that can
48 * contain certain tags representing the different supported configuration
49 * classes. If such a tag is found, the corresponding <code>Configuration</code>
50 * class is instantiated and initialized using the classes of the
51 * <code>beanutils</code> package (namely
52 * <code>{@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}</code>
53 * will be used to extract the configuration's initialization parameters, which
54 * allows for complex initialization szenarios).
55 * </p>
56 * <p>
57 * It is also possible to add custom tags to the configuration definition file.
58 * For this purpose register your own <code>ConfigurationProvider</code>
59 * implementation for your tag using the <code>addConfigurationProvider()</code>
60 * method. This provider will then be called when the corresponding custom tag
61 * is detected. For the default configuration classes providers are already
62 * registered.
63 * </p>
64 * <p>
65 * The configuration definition file has the following basic structure:
66 * </p>
67 * <p>
68 *
69 * <pre>
70 * <configuration>
71 * <header>
72 * <!-- Optional meta information about the composite configuration -->
73 * </header>
74 * <override>
75 * <!-- Declarations for override configurations -->
76 * </override>
77 * <additional>
78 * <!-- Declarations for union configurations -->
79 * </additional>
80 * </configuration>
81 * </pre>
82 *
83 * </p>
84 * <p>
85 * The name of the root element (here <code>configuration</code>) is
86 * arbitrary. There are two sections (both of them are optional) for declaring
87 * <em>override</em> and <em>additional</em> configurations. Configurations
88 * in the former section are evaluated in the order of their declaration, and
89 * properties of configurations declared earlier hide those of configurations
90 * declared later. Configurations in the latter section are combined to a union
91 * configuration, i.e. all of their properties are added to a large hierarchical
92 * configuration. Configuration declarations that occur as direct children of
93 * the root element are treated as override declarations.
94 * </p>
95 * <p>
96 * Each configuration declaration consists of a tag whose name is associated
97 * with a <code>ConfigurationProvider</code>. This can be one of the
98 * predefined tags like <code>properties</code>, or <code>xml</code>, or
99 * a custom tag, for which a configuration provider was registered. Attributes
100 * and sub elements with specific initialization parameters can be added. There
101 * are some reserved attributes with a special meaning that can be used in every
102 * configuration declaration:
103 * </p>
104 * <p>
105 * <table border="1">
106 * <tr>
107 * <th>Attribute</th>
108 * <th>Meaning</th>
109 * </tr>
110 * <tr>
111 * <td valign="top"><code>config-name</code></td>
112 * <td>Allows to specify a name for this configuration. This name can be used
113 * to obtain a reference to the configuration from the resulting combined
114 * configuration (see below).</td>
115 * </tr>
116 * <tr>
117 * <td valign="top"><code>config-at</code></td>
118 * <td>With this attribute an optional prefix can be specified for the
119 * properties of the corresponding configuration.</td>
120 * </tr>
121 * <tr>
122 * <td valign="top"><code>config-optional</code></td>
123 * <td>Declares a configuration as optional. This means that errors that occur
124 * when creating the configuration are ignored. (However
125 * <code>{@link org.apache.commons.configuration.event.ConfigurationErrorListener}</code>s
126 * registered at the builder instance will get notified about this error: they
127 * receive an event of type <code>EVENT_ERR_LOAD_OPTIONAL</code>. The key
128 * property of this event contains the name of the optional configuration source
129 * that caused this problem.)</td>
130 * </tr>
131 * </table>
132 * </p>
133 * <p>
134 * The optional <em>header</em> section can contain some meta data about the
135 * created configuration itself. For instance, it is possible to set further
136 * properties of the <code>NodeCombiner</code> objects used for constructing
137 * the resulting configuration.
138 * </p>
139 * <p>
140 * The configuration object returned by this builder is an instance of the
141 * <code>{@link CombinedConfiguration}</code> class. The return value of the
142 * <code>getConfiguration()</code> method can be casted to this type, and the
143 * <code>getConfiguration(boolean)</code> method directly declares
144 * <code>CombinedConfiguration</code> as return type. This allows for
145 * convenient access to the configuration objects maintained by the combined
146 * configuration (e.g. for updates of single configuration objects). It has also
147 * the advantage that the properties stored in all declared configuration
148 * objects are collected and transformed into a single hierarchical structure,
149 * which can be accessed using different expression engines.
150 * </p>
151 * <p>
152 * All declared override configurations are directly added to the resulting
153 * combined configuration. If they are given names (using the
154 * <code>config-name</code> attribute), they can directly be accessed using
155 * the <code>getConfiguration(String)</code> method of
156 * <code>CombinedConfiguration</code>. The additional configurations are
157 * alltogether added to another combined configuration, which uses a union
158 * combiner. Then this union configuration is added to the resulting combined
159 * configuration under the name defined by the <code>ADDITIONAL_NAME</code>
160 * constant.
161 * </p>
162 * <p>
163 * Implementation note: This class is not thread-safe. Especially the
164 * <code>getConfiguration()</code> methods should be called by a single thread
165 * only.
166 * </p>
167 *
168 * @since 1.3
169 * @author <a
170 * href="http://commons.apache.org/configuration/team-list.html">Commons
171 * Configuration team</a>
172 * @version $Id: DefaultConfigurationBuilder.java 595043 2007-11-14 20:44:43Z oheger $
173 */
174 public class DefaultConfigurationBuilder extends XMLConfiguration implements
175 ConfigurationBuilder
176 {
177 /**
178 * Constant for the name of the additional configuration. If the
179 * configuration definition file contains an <code>additional</code>
180 * section, a special union configuration is created and added under this
181 * name to the resulting combined configuration.
182 */
183 public static final String ADDITIONAL_NAME = DefaultConfigurationBuilder.class
184 .getName()
185 + "/ADDITIONAL_CONFIG";
186
187 /**
188 * Constant for the type of error events caused by optional configurations
189 * that cannot be loaded.
190 */
191 public static final int EVENT_ERR_LOAD_OPTIONAL = 51;
192
193 /** Constant for the name of the configuration bean factory. */
194 static final String CONFIG_BEAN_FACTORY_NAME = DefaultConfigurationBuilder.class
195 .getName()
196 + ".CONFIG_BEAN_FACTORY_NAME";
197
198 /** Constant for the reserved name attribute. */
199 static final String ATTR_NAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
200 + XMLBeanDeclaration.RESERVED_PREFIX
201 + "name"
202 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
203
204 /** Constant for the name of the at attribute. */
205 static final String ATTR_ATNAME = "at";
206
207 /** Constant for the reserved at attribute. */
208 static final String ATTR_AT_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
209 + XMLBeanDeclaration.RESERVED_PREFIX
210 + ATTR_ATNAME
211 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
212
213 /** Constant for the at attribute without the reserved prefix. */
214 static final String ATTR_AT = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
215 + ATTR_ATNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
216
217 /** Constant for the name of the optional attribute. */
218 static final String ATTR_OPTIONALNAME = "optional";
219
220 /** Constant for the reserved optional attribute. */
221 static final String ATTR_OPTIONAL_RES = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
222 + XMLBeanDeclaration.RESERVED_PREFIX
223 + ATTR_OPTIONALNAME
224 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
225
226 /** Constant for the optional attribute without the reserved prefix. */
227 static final String ATTR_OPTIONAL = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
228 + ATTR_OPTIONALNAME + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
229
230 /** Constant for the file name attribute. */
231 static final String ATTR_FILENAME = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
232 + "fileName" + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
233
234 /** Constant for the forceCreate attribute. */
235 static final String ATTR_FORCECREATE = DefaultExpressionEngine.DEFAULT_ATTRIBUTE_START
236 + XMLBeanDeclaration.RESERVED_PREFIX
237 + "forceCreate"
238 + DefaultExpressionEngine.DEFAULT_ATTRIBUTE_END;
239
240 /** Constant for the name of the header section. */
241 static final String SEC_HEADER = "header";
242
243 /** Constant for an expression that selects the union configurations. */
244 static final String KEY_UNION = "additional";
245
246 /** An array with the names of top level configuration sections.*/
247 static final String[] CONFIG_SECTIONS = {
248 "additional", "override", SEC_HEADER
249 };
250
251 /**
252 * Constant for an expression that selects override configurations in the
253 * override section.
254 */
255 static final String KEY_OVERRIDE = "override";
256
257 /**
258 * Constant for the key that points to the list nodes definition of the
259 * override combiner.
260 */
261 static final String KEY_OVERRIDE_LIST = SEC_HEADER
262 + ".combiner.override.list-nodes.node";
263
264 /**
265 * Constant for the key that points to the list nodes definition of the
266 * additional combiner.
267 */
268 static final String KEY_ADDITIONAL_LIST = SEC_HEADER
269 + ".combiner.additional.list-nodes.node";
270
271 /**
272 * Constant for the key of the result declaration. This key can point to a
273 * bean declaration, which defines properties of the resulting combined
274 * configuration.
275 */
276 static final String KEY_RESULT = SEC_HEADER + ".result";
277
278 /** Constant for the key of the combiner in the result declaration.*/
279 static final String KEY_COMBINER = KEY_RESULT + ".nodeCombiner";
280
281 /** Constant for the XML file extension. */
282 static final String EXT_XML = ".xml";
283
284 /** Constant for the provider for properties files. */
285 private static final ConfigurationProvider PROPERTIES_PROVIDER = new FileExtensionConfigurationProvider(
286 XMLPropertiesConfiguration.class, PropertiesConfiguration.class,
287 EXT_XML);
288
289 /** Constant for the provider for XML files. */
290 private static final ConfigurationProvider XML_PROVIDER = new XMLConfigurationProvider();
291
292 /** Constant for the provider for JNDI sources. */
293 private static final ConfigurationProvider JNDI_PROVIDER = new ConfigurationProvider(
294 JNDIConfiguration.class);
295
296 /** Constant for the provider for system properties. */
297 private static final ConfigurationProvider SYSTEM_PROVIDER = new ConfigurationProvider(
298 SystemConfiguration.class);
299
300 /** Constant for the provider for plist files. */
301 private static final ConfigurationProvider PLIST_PROVIDER = new FileExtensionConfigurationProvider(
302 "org.apache.commons.configuration.plist.XMLPropertyListConfiguration",
303 "org.apache.commons.configuration.plist.PropertyListConfiguration",
304 EXT_XML);
305
306 /** Constant for the provider for configuration definition files.*/
307 private static final ConfigurationProvider BUILDER_PROVIDER = new ConfigurationBuilderProvider();
308
309 /** An array with the names of the default tags. */
310 private static final String[] DEFAULT_TAGS =
311 {"properties", "xml", "hierarchicalXml", "jndi", "system", "plist", "configuration"};
312
313 /** An array with the providers for the default tags. */
314 private static final ConfigurationProvider[] DEFAULT_PROVIDERS =
315 {PROPERTIES_PROVIDER, XML_PROVIDER, XML_PROVIDER, JNDI_PROVIDER,
316 SYSTEM_PROVIDER, PLIST_PROVIDER, BUILDER_PROVIDER};
317
318 /**
319 * The serial version UID.
320 */
321 private static final long serialVersionUID = -3113777854714492123L;
322
323 /** Stores the configuration that is currently constructed.*/
324 private CombinedConfiguration constructedConfiguration;
325
326 /** Stores a map with the registered configuration providers. */
327 private Map providers;
328
329 /** Stores the base path to the configuration sources to load. */
330 private String configurationBasePath;
331
332 /**
333 * Creates a new instance of <code>DefaultConfigurationBuilder</code>. A
334 * configuration definition file is not yet loaded. Use the diverse setter
335 * methods provided by file based configurations to specify the
336 * configuration definition file.
337 */
338 public DefaultConfigurationBuilder()
339 {
340 super();
341 providers = new HashMap();
342 registerDefaultProviders();
343 registerBeanFactory();
344 setLogger(LogFactory.getLog(getClass()));
345 addErrorLogListener(); // log errors per default
346 }
347
348 /**
349 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
350 * sets the specified configuration definition file.
351 *
352 * @param file the configuration definition file
353 */
354 public DefaultConfigurationBuilder(File file)
355 {
356 this();
357 setFile(file);
358 }
359
360 /**
361 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
362 * sets the specified configuration definition file.
363 *
364 * @param fileName the name of the configuration definition file
365 * @throws ConfigurationException if an error occurs when the file is loaded
366 */
367 public DefaultConfigurationBuilder(String fileName)
368 throws ConfigurationException
369 {
370 this();
371 setFileName(fileName);
372 }
373
374 /**
375 * Creates a new instance of <code>DefaultConfigurationBuilder</code> and
376 * sets the specified configuration definition file.
377 *
378 * @param url the URL to the configuration definition file
379 * @throws ConfigurationException if an error occurs when the file is loaded
380 */
381 public DefaultConfigurationBuilder(URL url) throws ConfigurationException
382 {
383 this();
384 setURL(url);
385 }
386
387 /**
388 * Returns the base path for the configuration sources to load. This path is
389 * used to resolve relative paths in the configuration definition file.
390 *
391 * @return the base path for configuration sources
392 */
393 public String getConfigurationBasePath()
394 {
395 return (configurationBasePath != null) ? configurationBasePath
396 : getBasePath();
397 }
398
399 /**
400 * Sets the base path for the configuration sources to load. Normally a base
401 * path need not to be set because it is determined by the location of the
402 * configuration definition file to load. All relative pathes in this file
403 * are resolved relative to this file. Setting a base path makes sense if
404 * such relative pathes should be otherwise resolved, e.g. if the
405 * configuration file is loaded from the class path and all sub
406 * configurations it refers to are stored in a special config directory.
407 *
408 * @param configurationBasePath the new base path to set
409 */
410 public void setConfigurationBasePath(String configurationBasePath)
411 {
412 this.configurationBasePath = configurationBasePath;
413 }
414
415 /**
416 * Adds a configuration provider for the specified tag. Whenever this tag is
417 * encountered in the configuration definition file this provider will be
418 * called to create the configuration object.
419 *
420 * @param tagName the name of the tag in the configuration definition file
421 * @param provider the provider for this tag
422 */
423 public void addConfigurationProvider(String tagName,
424 ConfigurationProvider provider)
425 {
426 if (tagName == null)
427 {
428 throw new IllegalArgumentException("Tag name must not be null!");
429 }
430 if (provider == null)
431 {
432 throw new IllegalArgumentException("Provider must not be null!");
433 }
434
435 providers.put(tagName, provider);
436 }
437
438 /**
439 * Removes the configuration provider for the specified tag name.
440 *
441 * @param tagName the tag name
442 * @return the removed configuration provider or <b>null</b> if none was
443 * registered for that tag
444 */
445 public ConfigurationProvider removeConfigurationProvider(String tagName)
446 {
447 return (ConfigurationProvider) providers.remove(tagName);
448 }
449
450 /**
451 * Returns the configuration provider for the given tag.
452 *
453 * @param tagName the name of the tag
454 * @return the provider that was registered for this tag or <b>null</b> if
455 * there is none
456 */
457 public ConfigurationProvider providerForTag(String tagName)
458 {
459 return (ConfigurationProvider) providers.get(tagName);
460 }
461
462 /**
463 * Returns the configuration provided by this builder. Loads and parses the
464 * configuration definition file and creates instances for the declared
465 * configurations.
466 *
467 * @return the configuration
468 * @throws ConfigurationException if an error occurs
469 */
470 public Configuration getConfiguration() throws ConfigurationException
471 {
472 return getConfiguration(true);
473 }
474
475 /**
476 * Returns the configuration provided by this builder. If the boolean
477 * parameter is <b>true</b>, the configuration definition file will be
478 * loaded. It will then be parsed, and instances for the declared
479 * configurations will be created.
480 *
481 * @param load a flag whether the configuration definition file should be
482 * loaded; a value of <b>false</b> would make sense if the file has already
483 * been created or its content was manipulated using some of the property
484 * accessor methods
485 * @return the configuration
486 * @throws ConfigurationException if an error occurs
487 */
488 public CombinedConfiguration getConfiguration(boolean load)
489 throws ConfigurationException
490 {
491 if (load)
492 {
493 load();
494 }
495
496 CombinedConfiguration result = createResultConfiguration();
497 constructedConfiguration = result;
498
499 List overrides = fetchTopLevelOverrideConfigs();
500 overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
501 initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
502
503 List additionals = fetchChildConfigs(KEY_UNION);
504 if (!additionals.isEmpty())
505 {
506 CombinedConfiguration addConfig = new CombinedConfiguration(
507 new UnionCombiner());
508 result.addConfiguration(addConfig, ADDITIONAL_NAME);
509 initCombinedConfiguration(addConfig, additionals,
510 KEY_ADDITIONAL_LIST);
511 }
512
513 return result;
514 }
515
516 /**
517 * Creates the resulting combined configuration. This method is called by
518 * <code>getConfiguration()</code>. It checks whether the
519 * <code>header</code> section of the configuration definition file
520 * contains a <code>result</code> element. If this is the case, it will be
521 * used to initialize the properties of the newly created configuration
522 * object.
523 *
524 * @return the resulting configuration object
525 * @throws ConfigurationException if an error occurs
526 */
527 protected CombinedConfiguration createResultConfiguration()
528 throws ConfigurationException
529 {
530 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
531 CombinedConfiguration result = (CombinedConfiguration) BeanHelper
532 .createBean(decl, CombinedConfiguration.class);
533
534 if (getMaxIndex(KEY_COMBINER) < 0)
535 {
536 // No combiner defined => set default
537 result.setNodeCombiner(new OverrideCombiner());
538 }
539
540 return result;
541 }
542
543 /**
544 * Initializes a combined configuration for the configurations of a specific
545 * section. This method is called for the override and for the additional
546 * section (if it exists).
547 *
548 * @param config the configuration to be initialized
549 * @param containedConfigs the list with the declaratinos of the contained
550 * configurations
551 * @param keyListNodes a list with the declaration of list nodes
552 * @throws ConfigurationException if an error occurs
553 */
554 protected void initCombinedConfiguration(CombinedConfiguration config,
555 List containedConfigs, String keyListNodes) throws ConfigurationException
556 {
557 List listNodes = getList(keyListNodes);
558 for (Iterator it = listNodes.iterator(); it.hasNext();)
559 {
560 config.getNodeCombiner().addListNode((String) it.next());
561 }
562
563 for (Iterator it = containedConfigs.iterator(); it.hasNext();)
564 {
565 HierarchicalConfiguration conf = (HierarchicalConfiguration) it
566 .next();
567 ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
568 conf);
569 AbstractConfiguration newConf = createConfigurationAt(decl);
570 if (newConf != null)
571 {
572 config.addConfiguration(newConf, decl.getConfiguration()
573 .getString(ATTR_NAME), decl.getAt());
574 }
575 }
576 }
577
578 /**
579 * Registers the default configuration providers supported by this class.
580 * This method will be called during initialization. It registers
581 * configuration providers for the tags that are supported by default.
582 */
583 protected void registerDefaultProviders()
584 {
585 for (int i = 0; i < DEFAULT_TAGS.length; i++)
586 {
587 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
588 }
589 }
590
591 /**
592 * Performs interpolation. This method will not only take this configuration
593 * instance into account (which is the one that loaded the configuration
594 * definition file), but also the so far constructed combined configuration.
595 * So variables can be used that point to properties that are defined in
596 * configuration sources loaded by this builder.
597 *
598 * @param value the value to be interpolated
599 * @return the interpolated value
600 */
601 protected Object interpolate(Object value)
602 {
603 Object result = super.interpolate(value);
604 if (constructedConfiguration != null)
605 {
606 result = constructedConfiguration.interpolate(result);
607 }
608 return result;
609 }
610
611 protected void fireError(int type, String propName, Object propValue,
612 Throwable ex)
613 {
614 // This method is only overridden to fix a mysterious MethodNotFound
615 // error in the test cases when run under a JDK 1.3.
616 super.fireError(type, propName, propValue, ex);
617 }
618
619 /**
620 * Creates a configuration object from the specified configuration
621 * declaration.
622 *
623 * @param decl the configuration declaration
624 * @return the new configuration object
625 * @throws ConfigurationException if an error occurs
626 */
627 private AbstractConfiguration createConfigurationAt(
628 ConfigurationDeclaration decl) throws ConfigurationException
629 {
630 try
631 {
632 return (AbstractConfiguration) BeanHelper.createBean(decl);
633 }
634 catch (Exception ex)
635 {
636 // redirect to configuration exceptions
637 throw new ConfigurationException(ex);
638 }
639 }
640
641 /**
642 * Returns a list with <code>SubnodeConfiguration</code> objects for the
643 * child nodes of the specified configuration node.
644 *
645 * @param node the start node
646 * @return a list with subnode configurations for the node's children
647 */
648 private List fetchChildConfigs(ConfigurationNode node)
649 {
650 List children = node.getChildren();
651 List result = new ArrayList(children.size());
652 for (Iterator it = children.iterator(); it.hasNext();)
653 {
654 result.add(createSubnodeConfiguration((Node) it.next()));
655 }
656 return result;
657 }
658
659 /**
660 * Returns a list with <code>SubnodeConfiguration</code> objects for the
661 * child nodes of the node specified by the given key.
662 *
663 * @param key the key (must define exactly one node)
664 * @return a list with subnode configurations for the node's children
665 */
666 private List fetchChildConfigs(String key)
667 {
668 List nodes = fetchNodeList(key);
669 if (nodes.size() > 0)
670 {
671 return fetchChildConfigs((ConfigurationNode) nodes.get(0));
672 }
673 else
674 {
675 return Collections.EMPTY_LIST;
676 }
677 }
678
679 /**
680 * Finds the override configurations that are defined as top level elements
681 * in the configuration definition file. This method will fetch the child
682 * elements of the root node and remove the nodes that represent other
683 * configuration sections. The remaining nodes are treated as definitions
684 * for override configurations.
685 *
686 * @return a list with subnode configurations for the top level override
687 * configurations
688 */
689 private List fetchTopLevelOverrideConfigs()
690 {
691 List configs = fetchChildConfigs(getRootNode());
692 for (Iterator it = configs.iterator(); it.hasNext();)
693 {
694 String nodeName = ((SubnodeConfiguration) it.next()).getRootNode()
695 .getName();
696 for (int i = 0; i < CONFIG_SECTIONS.length; i++)
697 {
698 if (CONFIG_SECTIONS[i].equals(nodeName))
699 {
700 it.remove();
701 break;
702 }
703 }
704 }
705 return configs;
706 }
707
708 /**
709 * Registers the bean factory used by this class if necessary. This method
710 * is called by the constructor to ensure that the required bean factory is
711 * available.
712 */
713 private void registerBeanFactory()
714 {
715 synchronized (getClass())
716 {
717 if (!BeanHelper.registeredFactoryNames().contains(
718 CONFIG_BEAN_FACTORY_NAME))
719 {
720 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
721 new ConfigurationBeanFactory());
722 }
723 }
724 }
725
726 /**
727 * <p>
728 * A base class for creating and initializing configuration sources.
729 * </p>
730 * <p>
731 * Concrete sub classes of this base class are responsible for creating
732 * specific <code>Configuration</code> objects for the tags in the
733 * configuration definition file. The configuration factory will parse the
734 * definition file and try to find a matching
735 * <code>ConfigurationProvider</code> for each encountered tag. This
736 * provider is then asked to create a corresponding
737 * <code>Configuration</code> object. It is up to a concrete
738 * implementation how this object is created and initialized.
739 * </p>
740 * <p>
741 * Note that at the moment only configuration classes derived from
742 * <code>{@link AbstractConfiguration}</code> are supported.
743 * </p>
744 */
745 public static class ConfigurationProvider extends DefaultBeanFactory
746 {
747 /** Stores the class of the configuration to be created. */
748 private Class configurationClass;
749
750 /** Stores the name of the configuration class to be created.*/
751 private String configurationClassName;
752
753 /**
754 * Creates a new uninitialized instance of
755 * <code>ConfigurationProvider</code>.
756 */
757 public ConfigurationProvider()
758 {
759 this((Class) null);
760 }
761
762 /**
763 * Creates a new instance of <code>ConfigurationProvider</code> and
764 * sets the class of the configuration created by this provider.
765 *
766 * @param configClass the configuration class
767 */
768 public ConfigurationProvider(Class configClass)
769 {
770 setConfigurationClass(configClass);
771 }
772
773 /**
774 * Creates a new instance of <code>ConfigurationProvider</code> and
775 * sets the name of the class of the configuration created by this
776 * provider.
777 *
778 * @param configClassName the name of the configuration class
779 * @since 1.4
780 */
781 public ConfigurationProvider(String configClassName)
782 {
783 setConfigurationClassName(configClassName);
784 }
785
786 /**
787 * Returns the class of the configuration returned by this provider.
788 *
789 * @return the class of the provided configuration
790 */
791 public Class getConfigurationClass()
792 {
793 return configurationClass;
794 }
795
796 /**
797 * Sets the class of the configuration returned by this provider.
798 *
799 * @param configurationClass the configuration class
800 */
801 public void setConfigurationClass(Class configurationClass)
802 {
803 this.configurationClass = configurationClass;
804 }
805
806 /**
807 * Returns the name of the configuration class returned by this
808 * provider.
809 *
810 * @return the configuration class name
811 * @since 1.4
812 */
813 public String getConfigurationClassName()
814 {
815 return configurationClassName;
816 }
817
818 /**
819 * Sets the name of the configuration class returned by this provider.
820 *
821 * @param configurationClassName the name of the configuration class
822 * @since 1.4
823 */
824 public void setConfigurationClassName(String configurationClassName)
825 {
826 this.configurationClassName = configurationClassName;
827 }
828
829 /**
830 * Returns the configuration. This method is called to fetch the
831 * configuration from the provider. This implementation will call the
832 * inherited <code>{@link
833 * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
834 * createBean()}</code> method to create a new instance of the
835 * configuration class.
836 *
837 * @param decl the bean declaration with initialization parameters for
838 * the configuration
839 * @return the new configuration object
840 * @throws Exception if an error occurs
841 */
842 public AbstractConfiguration getConfiguration(
843 ConfigurationDeclaration decl) throws Exception
844 {
845 return (AbstractConfiguration) createBean(fetchConfigurationClass(),
846 decl, null);
847 }
848
849 /**
850 * Returns an uninitialized configuration of the represented type. This
851 * method will be called for optional configurations when the
852 * <code>getConfiguration()</code> method caused an error and the
853 * <code>forceCreate</code> attribute is set. A concrete sub class can
854 * here try to create an uninitialized, empty configuration, which may
855 * be possible if the error was created during initialization. This base
856 * implementation just returns <b>null</b>.
857 *
858 * @param decl the bean declaration with initialization parameters for
859 * the configuration
860 * @return the new configuration object
861 * @throws Exception if an error occurs
862 * @since 1.4
863 */
864 public AbstractConfiguration getEmptyConfiguration(
865 ConfigurationDeclaration decl) throws Exception
866 {
867 return null;
868 }
869
870 /**
871 * Returns the configuration class supported by this provider. If a
872 * class object was set, it is returned. Otherwise the method tries to
873 * resolve the class name.
874 *
875 * @return the class of the configuration to be created
876 * @since 1.4
877 */
878 protected synchronized Class fetchConfigurationClass() throws Exception
879 {
880 if (getConfigurationClass() == null)
881 {
882 setConfigurationClass(loadClass(getConfigurationClassName()));
883 }
884 return getConfigurationClass();
885 }
886
887 /**
888 * Loads the class with the specified name dynamically. If the class's
889 * name is <b>null</b>, <b>null</b> will also be returned.
890 *
891 * @param className the name of the class to be loaded
892 * @return the class object
893 * @throws ClassNotFoundException if class loading fails
894 * @since 1.4
895 */
896 protected Class loadClass(String className)
897 throws ClassNotFoundException
898 {
899 return (className != null) ? Class.forName(className, true,
900 getClass().getClassLoader()) : null;
901 }
902 }
903
904 /**
905 * <p>
906 * A specialized <code>BeanDeclaration</code> implementation that
907 * represents the declaration of a configuration source.
908 * </p>
909 * <p>
910 * Instances of this class are able to extract all information about a
911 * configuration source from the configuration definition file. The
912 * declaration of a configuration source is very similar to a bean
913 * declaration processed by <code>XMLBeanDeclaration</code>. There are
914 * very few differences, e.g. some reserved attributes like
915 * <code>optional</code> and <code>at</code> and the fact that a bean
916 * factory is never needed.
917 * </p>
918 */
919 public static class ConfigurationDeclaration extends XMLBeanDeclaration
920 {
921 /** Stores a reference to the associated configuration builder. */
922 private DefaultConfigurationBuilder configurationBuilder;
923
924 /**
925 * Creates a new instance of <code>ConfigurationDeclaration</code> and
926 * initializes it.
927 *
928 * @param builder the associated configuration builder
929 * @param config the configuration this declaration is based onto
930 */
931 public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
932 HierarchicalConfiguration config)
933 {
934 super(config);
935 configurationBuilder = builder;
936 }
937
938 /**
939 * Returns the associated configuration builder.
940 *
941 * @return the configuration builder
942 */
943 public DefaultConfigurationBuilder getConfigurationBuilder()
944 {
945 return configurationBuilder;
946 }
947
948 /**
949 * Returns the value of the <code>at</code> attribute.
950 *
951 * @return the value of the <code>at</code> attribute (can be <b>null</b>)
952 */
953 public String getAt()
954 {
955 String result = this.getConfiguration().getString(ATTR_AT_RES);
956 return (result == null) ? this.getConfiguration().getString(ATTR_AT)
957 : result;
958 }
959
960 /**
961 * Returns a flag whether this is an optional configuration.
962 *
963 * @return a flag if this declaration points to an optional
964 * configuration
965 */
966 public boolean isOptional()
967 {
968 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
969 null);
970 if (value == null)
971 {
972 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
973 Boolean.FALSE);
974 }
975 return value.booleanValue();
976 }
977
978 /**
979 * Returns a flag whether this configuration should always be created
980 * and added to the resulting combined configuration. This flag is
981 * evaluated only for optional configurations whose normal creation has
982 * caused an error. If for such a configuration the
983 * <code>forceCreate</code> attribute is set and the corresponding
984 * configuration provider supports this mode, an empty configuration
985 * will be created and added to the resulting combined configuration.
986 *
987 * @return the value of the <code>forceCreate</code> attribute
988 * @since 1.4
989 */
990 public boolean isForceCreate()
991 {
992 return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
993 }
994
995 /**
996 * Returns the name of the bean factory. For configuration source
997 * declarations always a reserved factory is used. This factory's name
998 * is returned by this implementation.
999 *
1000 * @return the name of the bean factory
1001 */
1002 public String getBeanFactoryName()
1003 {
1004 return CONFIG_BEAN_FACTORY_NAME;
1005 }
1006
1007 /**
1008 * Returns the bean's class name. This implementation will always return
1009 * <b>null</b>.
1010 *
1011 * @return the name of the bean's class
1012 */
1013 public String getBeanClassName()
1014 {
1015 return null;
1016 }
1017
1018 /**
1019 * Checks whether the given node is reserved. This method will take
1020 * further reserved attributes into account
1021 *
1022 * @param nd the node
1023 * @return a flag whether this node is reserved
1024 */
1025 protected boolean isReservedNode(ConfigurationNode nd)
1026 {
1027 if (super.isReservedNode(nd))
1028 {
1029 return true;
1030 }
1031
1032 return nd.isAttribute()
1033 && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
1034 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
1035 .equals(nd.getName()) && nd.getParentNode()
1036 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
1037 }
1038
1039 /**
1040 * Performs interpolation. This implementation will delegate
1041 * interpolation to the configuration builder, which takes care that the
1042 * currently constructed configuration is taken into account, too.
1043 *
1044 * @param value the value to be interpolated
1045 * @return the interpolated value
1046 */
1047 protected Object interpolate(Object value)
1048 {
1049 return getConfigurationBuilder().interpolate(value);
1050 }
1051 }
1052
1053 /**
1054 * A specialized <code>BeanFactory</code> implementation that handles
1055 * configuration declarations. This class will retrieve the correct
1056 * configuration provider and delegate the task of creating the
1057 * configuration to this object.
1058 */
1059 static class ConfigurationBeanFactory implements BeanFactory
1060 {
1061 /**
1062 * Creates an instance of a bean class. This implementation expects that
1063 * the passed in bean declaration is a declaration for a configuration.
1064 * It will determine the responsible configuration provider and delegate
1065 * the call to this instance. If creation of the configuration fails
1066 * and the <code>optional</code> attribute is set, the exception will
1067 * be ignored. If the <code>forceCreate</code> attribute is set, too,
1068 * the provider is asked to create an empty configuration. A return
1069 * value of <b>null</b> means that no configuration could be created.
1070 *
1071 * @param beanClass the bean class (will be ignored)
1072 * @param data the declaration
1073 * @param param an additional parameter (will be ignored)
1074 * @return the newly created configuration
1075 * @throws Exception if an error occurs
1076 */
1077 public Object createBean(Class beanClass, BeanDeclaration data,
1078 Object param) throws Exception
1079 {
1080 ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1081 String tagName = decl.getNode().getName();
1082 ConfigurationProvider provider = decl.getConfigurationBuilder()
1083 .providerForTag(tagName);
1084 if (provider == null)
1085 {
1086 throw new ConfigurationRuntimeException(
1087 "No ConfigurationProvider registered for tag "
1088 + tagName);
1089 }
1090
1091 try
1092 {
1093 return provider.getConfiguration(decl);
1094 }
1095 catch (Exception ex)
1096 {
1097 // If this is an optional configuration, ignore the exception
1098 if (!decl.isOptional())
1099 {
1100 throw ex;
1101 }
1102 else
1103 {
1104 // Notify registered error listeners
1105 decl.getConfigurationBuilder().fireError(
1106 EVENT_ERR_LOAD_OPTIONAL,
1107 decl.getConfiguration().getString(ATTR_NAME), null,
1108 ex);
1109
1110 if (decl.isForceCreate())
1111 {
1112 try
1113 {
1114 return provider.getEmptyConfiguration(decl);
1115 }
1116 catch (Exception ex2)
1117 {
1118 // Ignore exception, return null in this case
1119 ;
1120 }
1121 }
1122 return null;
1123 }
1124 }
1125 }
1126
1127 /**
1128 * Returns the default class for this bean factory.
1129 *
1130 * @return the default class
1131 */
1132 public Class getDefaultBeanClass()
1133 {
1134 // Here some valid class must be returned, otherwise BeanHelper
1135 // will complain that the bean's class cannot be determined
1136 return Configuration.class;
1137 }
1138 }
1139
1140 /**
1141 * A specialized provider implementation that deals with file based
1142 * configurations. Ensures that the base path is correctly set and that the
1143 * load() method gets called.
1144 */
1145 public static class FileConfigurationProvider extends ConfigurationProvider
1146 {
1147 /**
1148 * Creates a new instance of <code>FileConfigurationProvider</code>.
1149 */
1150 public FileConfigurationProvider()
1151 {
1152 super();
1153 }
1154
1155 /**
1156 * Creates a new instance of <code>FileConfigurationProvider</code>
1157 * and sets the configuration class.
1158 *
1159 * @param configClass the class for the configurations to be created
1160 */
1161 public FileConfigurationProvider(Class configClass)
1162 {
1163 super(configClass);
1164 }
1165
1166 /**
1167 * Creates a new instance of <code>FileConfigurationProvider</code>
1168 * and sets the configuration class name.
1169 *
1170 * @param configClassName the name of the configuration to be created
1171 * @since 1.4
1172 */
1173 public FileConfigurati