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.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();
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
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
615
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
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
1098 if (!decl.isOptional())
1099 {
1100 throw ex;
1101 }
1102 else
1103 {
1104
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
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
1135
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 FileConfigurationProvider(String configClassName)
1174 {
1175 super(configClassName);
1176 }
1177
1178 /***
1179 * Creates the configuration. After that <code>load()</code> will be
1180 * called. If this configuration is marked as optional, exceptions will
1181 * be ignored.
1182 *
1183 * @param decl the declaration
1184 * @return the new configuration
1185 * @throws Exception if an error occurs
1186 */
1187 public AbstractConfiguration getConfiguration(
1188 ConfigurationDeclaration decl) throws Exception
1189 {
1190 AbstractConfiguration result = getEmptyConfiguration(decl);
1191 ((FileConfiguration) result).load();
1192 return result;
1193 }
1194
1195 /***
1196 * Returns an uninitialized file configuration. This method will be
1197 * called for optional configurations when the
1198 * <code>getConfiguration()</code> method caused an error and the
1199 * <code>forceCreate</code> attribute is set. It will create the
1200 * configuration of the represented type, but the <code>load()</code>
1201 * method won't be called. This way non-existing configuration files can
1202 * be handled gracefully: If loading a the file fails, an empty
1203 * configuration will be created that is already configured with the
1204 * correct file name.
1205 *
1206 * @param decl the bean declaration with initialization parameters for
1207 * the configuration
1208 * @return the new configuration object
1209 * @throws Exception if an error occurs
1210 * @since 1.4
1211 */
1212 public AbstractConfiguration getEmptyConfiguration(
1213 ConfigurationDeclaration decl) throws Exception
1214 {
1215 return super.getConfiguration(decl);
1216 }
1217
1218 /***
1219 * Initializes the bean instance. Ensures that the file configuration's
1220 * base path will be initialized with the base path of the factory so
1221 * that relative path names can be correctly resolved.
1222 *
1223 * @param bean the bean to be initialized
1224 * @param data the declaration
1225 * @throws Exception if an error occurs
1226 */
1227 protected void initBeanInstance(Object bean, BeanDeclaration data)
1228 throws Exception
1229 {
1230 FileConfiguration config = (FileConfiguration) bean;
1231 config.setBasePath(((ConfigurationDeclaration) data)
1232 .getConfigurationBuilder().getConfigurationBasePath());
1233 super.initBeanInstance(bean, data);
1234 }
1235 }
1236
1237 /***
1238 * A specialized configuration provider for XML configurations. This
1239 * implementation acts like a <code>FileConfigurationProvider</code>, but
1240 * it will copy all entity IDs that have been registered for the
1241 * configuration builder to the new XML configuration before it is loaded.
1242 */
1243 static class XMLConfigurationProvider extends FileConfigurationProvider
1244 {
1245 /***
1246 * Creates a new instance of <code>XMLConfigurationProvider</code>.
1247 */
1248 public XMLConfigurationProvider()
1249 {
1250 super(XMLConfiguration.class);
1251 }
1252
1253 /***
1254 * Returns a new empty configuration instance. This implementation
1255 * performs some additional initialization specific to XML
1256 * configurations.
1257 *
1258 * @param decl the configuration declaration
1259 * @return the new configuration
1260 * @throws Exception if an error occurs
1261 */
1262 public AbstractConfiguration getEmptyConfiguration(
1263 ConfigurationDeclaration decl) throws Exception
1264 {
1265 XMLConfiguration config = (XMLConfiguration) super
1266 .getEmptyConfiguration(decl);
1267
1268
1269 DefaultConfigurationBuilder builder = decl
1270 .getConfigurationBuilder();
1271 config.getRegisteredEntities().putAll(
1272 builder.getRegisteredEntities());
1273 return config;
1274 }
1275 }
1276
1277 /***
1278 * A specialized configuration provider for file based configurations that
1279 * can handle configuration sources whose concrete type depends on the
1280 * extension of the file to be loaded. One example is the
1281 * <code>properties</code> tag: if the file ends with ".xml" a
1282 * XMLPropertiesConfiguration object must be created, otherwise a
1283 * PropertiesConfiguration object.
1284 */
1285 static class FileExtensionConfigurationProvider extends
1286 FileConfigurationProvider
1287 {
1288 /***
1289 * Stores the class to be created when the file extension matches.
1290 */
1291 private Class matchingClass;
1292
1293 /***
1294 * Stores the name of the class to be created when the file extension
1295 * matches.
1296 */
1297 private String matchingClassName;
1298
1299 /***
1300 * Stores the class to be created when the file extension does not
1301 * match.
1302 */
1303 private Class defaultClass;
1304
1305 /***
1306 * Stores the name of the class to be created when the file extension
1307 * does not match.
1308 */
1309 private String defaultClassName;
1310
1311 /*** Stores the file extension to be checked against. */
1312 private String fileExtension;
1313
1314 /***
1315 * Creates a new instance of
1316 * <code>FileExtensionConfigurationProvider</code> and initializes it.
1317 *
1318 * @param matchingClass the class to be created when the file extension
1319 * matches
1320 * @param defaultClass the class to be created when the file extension
1321 * does not match
1322 * @param extension the file extension to be checked agains
1323 */
1324 public FileExtensionConfigurationProvider(Class matchingClass,
1325 Class defaultClass, String extension)
1326 {
1327 this.matchingClass = matchingClass;
1328 this.defaultClass = defaultClass;
1329 fileExtension = extension;
1330 }
1331
1332 /***
1333 * Creates a new instance of
1334 * <code>FileExtensionConfigurationProvider</code> and initializes it
1335 * with the names of the classes to be created.
1336 *
1337 * @param matchingClassName the name of the class to be created when the
1338 * file extension matches
1339 * @param defaultClassName the name of the class to be created when the
1340 * file extension does not match
1341 * @param extension the file extension to be checked against
1342 * @since 1.4
1343 */
1344 public FileExtensionConfigurationProvider(String matchingClassName,
1345 String defaultClassName, String extension)
1346 {
1347 this.matchingClassName = matchingClassName;
1348 this.defaultClassName = defaultClassName;
1349 fileExtension = extension;
1350 }
1351
1352 /***
1353 * Returns the matching class object, no matter whether it was defined
1354 * as a class or as a class name.
1355 *
1356 * @return the matching class object
1357 * @throws Exception if an error occurs
1358 * @since 1.4
1359 */
1360 protected synchronized Class fetchMatchingClass() throws Exception
1361 {
1362 if (matchingClass == null)
1363 {
1364 matchingClass = loadClass(matchingClassName);
1365 }
1366 return matchingClass;
1367 }
1368
1369 /***
1370 * Returns the default class object, no matter whether it was defined as
1371 * a class or as a class name.
1372 *
1373 * @return the default class object
1374 * @throws Exception if an error occurs
1375 * @since 1.4
1376 */
1377 protected synchronized Class fetchDefaultClass() throws Exception
1378 {
1379 if (defaultClass == null)
1380 {
1381 defaultClass = loadClass(defaultClassName);
1382 }
1383 return defaultClass;
1384 }
1385
1386 /***
1387 * Creates the configuration object. The class is determined by the file
1388 * name's extension.
1389 *
1390 * @param beanClass the class
1391 * @param data the bean declaration
1392 * @return the new bean
1393 * @throws Exception if an error occurs
1394 */
1395 protected Object createBeanInstance(Class beanClass,
1396 BeanDeclaration data) throws Exception
1397 {
1398 String fileName = ((ConfigurationDeclaration) data)
1399 .getConfiguration().getString(ATTR_FILENAME);
1400 if (fileName != null
1401 && fileName.toLowerCase().trim().endsWith(fileExtension))
1402 {
1403 return super.createBeanInstance(fetchMatchingClass(), data);
1404 }
1405 else
1406 {
1407 return super.createBeanInstance(fetchDefaultClass(), data);
1408 }
1409 }
1410 }
1411
1412 /***
1413 * A specialized configuration provider class that allows to include other
1414 * configuration definition files.
1415 */
1416 static class ConfigurationBuilderProvider extends ConfigurationProvider
1417 {
1418 /***
1419 * Creates a new instance of <code>ConfigurationBuilderProvider</code>.
1420 */
1421 public ConfigurationBuilderProvider()
1422 {
1423 super(DefaultConfigurationBuilder.class);
1424 }
1425
1426 /***
1427 * Creates the configuration. First creates a configuration builder
1428 * object. Then returns the configuration created by this builder.
1429 *
1430 * @param decl the configuration declaration
1431 * @return the configuration
1432 * @exception Exception if an error occurs
1433 */
1434 public AbstractConfiguration getConfiguration(
1435 ConfigurationDeclaration decl) throws Exception
1436 {
1437 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
1438 .getConfiguration(decl);
1439 return builder.getConfiguration(true);
1440 }
1441
1442 /***
1443 * Returns an empty configuration in case of an optional configuration
1444 * could not be created. This implementation returns an empty combined
1445 * configuration.
1446 *
1447 * @param decl the configuration declaration
1448 * @return the configuration
1449 * @exception Exception if an error occurs
1450 * @since 1.4
1451 */
1452 public AbstractConfiguration getEmptyConfiguration(
1453 ConfigurationDeclaration decl) throws Exception
1454 {
1455 return new CombinedConfiguration();
1456 }
1457 }
1458 }