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