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}
059 * class is instantiated and initialized using the classes of the
060 * {@code beanutils} package (namely
061 * {@link org.apache.commons.configuration.beanutils.XMLBeanDeclaration XMLBeanDeclaration}
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}
068 * implementation for your tag using the {@code addConfigurationProvider()}
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}) 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}. This can be one of the
113 * predefined tags like {@code properties}, or {@code xml}, 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}</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}</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}</td>
138 * <td>Declares a configuration as optional. This means that errors that occur
139 * when creating the configuration are ignored. (However
140 * {@link org.apache.commons.configuration.event.ConfigurationErrorListener}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}. 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} 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 * {@link CombinedConfiguration} class. The return value of the
157 * {@code getConfiguration()} method can be casted to this type, and the
158 * {@code getConfiguration(boolean)} method directly declares
159 * {@code CombinedConfiguration} 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} attribute), they can directly be accessed using
200 * the {@code getConfiguration(String)} method of
201 * {@code CombinedConfiguration}. 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}
205 * constant.
206 * </p>
207 * <p>
208 * Implementation note: This class is not thread-safe. Especially the
209 * {@code getConfiguration()} 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 1208782 2011-11-30 21:12:00Z 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}
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<String, ConfigurationProvider> 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}. 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<String, ConfigurationProvider>();
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} 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} 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} 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<SubnodeConfiguration> overrides = fetchTopLevelOverrideConfigs();
600 overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
601 initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
602
603 List<SubnodeConfiguration> 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()}. It checks whether the
618 * {@code header} section of the configuration definition file
619 * contains a {@code result} 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} 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} 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} 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<? extends HierarchicalConfiguration> containedConfigs,
681 String keyListNodes) throws ConfigurationException
682 {
683 List<Object> listNodes = getList(keyListNodes);
684 for (Object listNode : listNodes)
685 {
686 config.getNodeCombiner().addListNode((String) listNode);
687 }
688
689 for (HierarchicalConfiguration conf : containedConfigs)
690 {
691 ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
692 conf);
693 if (getLogger().isDebugEnabled())
694 {
695 getLogger().debug("Creating configuration " + decl.getBeanClassName() + " with name "
696 + decl.getConfiguration().getString(ATTR_NAME));
697 }
698 AbstractConfiguration newConf = createConfigurationAt(decl);
699 if (newConf != null)
700 {
701 config.addConfiguration(newConf, decl.getConfiguration()
702 .getString(ATTR_NAME), decl.getAt());
703 }
704 }
705 }
706
707 /**
708 * Registers the default configuration providers supported by this class.
709 * This method will be called during initialization. It registers
710 * configuration providers for the tags that are supported by default.
711 */
712 protected void registerDefaultProviders()
713 {
714 for (int i = 0; i < DEFAULT_TAGS.length; i++)
715 {
716 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
717 }
718 }
719
720 /**
721 * Registers providers defined in the configuration.
722 *
723 * @throws ConfigurationException if an error occurs
724 */
725 protected void registerConfiguredProviders() throws ConfigurationException
726 {
727 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS);
728 for (HierarchicalConfiguration config : nodes)
729 {
730 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
731 String key = config.getString(KEY_PROVIDER_KEY);
732 addConfigurationProvider(key, (ConfigurationProvider) BeanHelper
733 .createBean(decl));
734 }
735 }
736
737 /**
738 * Registers StrLookups defined in the configuration.
739 *
740 * @throws ConfigurationException if an error occurs
741 */
742 protected void registerConfiguredLookups() throws ConfigurationException
743 {
744 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS);
745 for (HierarchicalConfiguration config : nodes)
746 {
747 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
748 String key = config.getString(KEY_LOOKUP_KEY);
749 StrLookup lookup = (StrLookup) BeanHelper.createBean(decl);
750 BeanHelper.setProperty(lookup, "configuration", this);
751 ConfigurationInterpolator.registerGlobalLookup(key, lookup);
752 this.getInterpolator().registerLookup(key, lookup);
753 }
754 }
755
756 protected void initFileSystem() throws ConfigurationException
757 {
758 if (getMaxIndex(FILE_SYSTEM) == 0)
759 {
760 HierarchicalConfiguration config = configurationAt(FILE_SYSTEM);
761 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
762 setFileSystem((FileSystem) BeanHelper.createBean(decl));
763 }
764 }
765
766 /**
767 * If a property file is configured add the properties to the System properties.
768 * @throws ConfigurationException if an error occurs.
769 */
770 protected void initSystemProperties() throws ConfigurationException
771 {
772 String fileName = getString(KEY_SYSTEM_PROPS);
773 if (fileName != null)
774 {
775 try
776 {
777 SystemConfiguration.setSystemProperties(getConfigurationBasePath(), fileName);
778 }
779 catch (Exception ex)
780 {
781 throw new ConfigurationException("Error setting system properties from " + fileName, ex);
782 }
783
784 }
785 }
786
787 protected void configureEntityResolver() throws ConfigurationException
788 {
789 if (getMaxIndex(KEY_ENTITY_RESOLVER) == 0)
790 {
791 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_ENTITY_RESOLVER, true);
792 EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class);
793 BeanHelper.setProperty(resolver, "fileSystem", getFileSystem());
794 BeanHelper.setProperty(resolver, "baseDir", getBasePath());
795 BeanHelper.setProperty(resolver, "substitutor", getSubstitutor());
796 setEntityResolver(resolver);
797 }
798 }
799
800 /**
801 * Performs interpolation. This method will not only take this configuration
802 * instance into account (which is the one that loaded the configuration
803 * definition file), but also the so far constructed combined configuration.
804 * So variables can be used that point to properties that are defined in
805 * configuration sources loaded by this builder.
806 *
807 * @param value the value to be interpolated
808 * @return the interpolated value
809 */
810 @Override
811 protected Object interpolate(Object value)
812 {
813 Object result = super.interpolate(value);
814 if (constructedConfiguration != null)
815 {
816 result = constructedConfiguration.interpolate(result);
817 }
818 return result;
819 }
820
821 /**
822 * Creates a configuration object from the specified configuration
823 * declaration.
824 *
825 * @param decl the configuration declaration
826 * @return the new configuration object
827 * @throws ConfigurationException if an error occurs
828 */
829 private AbstractConfiguration createConfigurationAt(
830 ConfigurationDeclaration decl) throws ConfigurationException
831 {
832 try
833 {
834 return (AbstractConfiguration) BeanHelper.createBean(decl);
835 }
836 catch (Exception ex)
837 {
838 // redirect to configuration exceptions
839 throw new ConfigurationException(ex);
840 }
841 }
842
843 /**
844 * Returns a list with {@code SubnodeConfiguration} objects for the
845 * child nodes of the specified configuration node.
846 *
847 * @param node the start node
848 * @return a list with subnode configurations for the node's children
849 */
850 private List<SubnodeConfiguration> fetchChildConfigs(ConfigurationNode node)
851 {
852 List<ConfigurationNode> children = node.getChildren();
853 List<SubnodeConfiguration> result = new ArrayList<SubnodeConfiguration>(children.size());
854 for (ConfigurationNode child : children)
855 {
856 result.add(createSubnodeConfiguration(child));
857 }
858 return result;
859 }
860
861 /**
862 * Returns a list with {@code SubnodeConfiguration} objects for the
863 * child nodes of the node specified by the given key.
864 *
865 * @param key the key (must define exactly one node)
866 * @return a list with subnode configurations for the node's children
867 */
868 private List<SubnodeConfiguration> fetchChildConfigs(String key)
869 {
870 List<ConfigurationNode> nodes = fetchNodeList(key);
871 if (nodes.size() > 0)
872 {
873 return fetchChildConfigs(nodes.get(0));
874 }
875 else
876 {
877 return Collections.emptyList();
878 }
879 }
880
881 /**
882 * Finds the override configurations that are defined as top level elements
883 * in the configuration definition file. This method will fetch the child
884 * elements of the root node and remove the nodes that represent other
885 * configuration sections. The remaining nodes are treated as definitions
886 * for override configurations.
887 *
888 * @return a list with subnode configurations for the top level override
889 * configurations
890 */
891 private List<SubnodeConfiguration> fetchTopLevelOverrideConfigs()
892 {
893 List<SubnodeConfiguration> configs = fetchChildConfigs(getRootNode());
894 for (Iterator<SubnodeConfiguration> it = configs.iterator(); it.hasNext();)
895 {
896 String nodeName = it.next().getRootNode().getName();
897 for (int i = 0; i < CONFIG_SECTIONS.length; i++)
898 {
899 if (CONFIG_SECTIONS[i].equals(nodeName))
900 {
901 it.remove();
902 break;
903 }
904 }
905 }
906 return configs;
907 }
908
909 /**
910 * Registers the bean factory used by this class if necessary. This method
911 * is called by the constructor to ensure that the required bean factory is
912 * available.
913 */
914 private void registerBeanFactory()
915 {
916 synchronized (DefaultConfigurationBuilder.class)
917 {
918 if (!BeanHelper.registeredFactoryNames().contains(
919 CONFIG_BEAN_FACTORY_NAME))
920 {
921 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
922 new ConfigurationBeanFactory());
923 }
924 }
925 }
926
927 /**
928 * <p>
929 * A base class for creating and initializing configuration sources.
930 * </p>
931 * <p>
932 * Concrete sub classes of this base class are responsible for creating
933 * specific {@code Configuration} objects for the tags in the
934 * configuration definition file. The configuration factory will parse the
935 * definition file and try to find a matching
936 * {@code ConfigurationProvider} for each encountered tag. This
937 * provider is then asked to create a corresponding
938 * {@code Configuration} object. It is up to a concrete
939 * implementation how this object is created and initialized.
940 * </p>
941 * <p>
942 * Note that at the moment only configuration classes derived from
943 * {@link AbstractConfiguration} are supported.
944 * </p>
945 */
946 public static class ConfigurationProvider extends DefaultBeanFactory
947 {
948 /** Stores the class of the configuration to be created. */
949 private Class<?> configurationClass;
950
951 /** Stores the name of the configuration class to be created.*/
952 private String configurationClassName;
953
954 /**
955 * Creates a new uninitialized instance of {@code ConfigurationProvider}.
956 */
957 public ConfigurationProvider()
958 {
959 this((Class<?>) null);
960 }
961
962 /**
963 * Creates a new instance of {@code ConfigurationProvider} and
964 * sets the class of the configuration created by this provider.
965 *
966 * @param configClass the configuration class
967 */
968 public ConfigurationProvider(Class<?> configClass)
969 {
970 setConfigurationClass(configClass);
971 }
972
973 /**
974 * Creates a new instance of {@code ConfigurationProvider} and
975 * sets the name of the class of the configuration created by this
976 * provider.
977 *
978 * @param configClassName the name of the configuration class
979 * @since 1.4
980 */
981 public ConfigurationProvider(String configClassName)
982 {
983 setConfigurationClassName(configClassName);
984 }
985
986 /**
987 * Returns the class of the configuration returned by this provider.
988 *
989 * @return the class of the provided configuration
990 */
991 public Class<?> getConfigurationClass()
992 {
993 return configurationClass;
994 }
995
996 /**
997 * Sets the class of the configuration returned by this provider.
998 *
999 * @param configurationClass the configuration class
1000 */
1001 public void setConfigurationClass(Class<?> configurationClass)
1002 {
1003 this.configurationClass = configurationClass;
1004 }
1005
1006 /**
1007 * Returns the name of the configuration class returned by this
1008 * provider.
1009 *
1010 * @return the configuration class name
1011 * @since 1.4
1012 */
1013 public String getConfigurationClassName()
1014 {
1015 return configurationClassName;
1016 }
1017
1018 /**
1019 * Sets the name of the configuration class returned by this provider.
1020 *
1021 * @param configurationClassName the name of the configuration class
1022 * @since 1.4
1023 */
1024 public void setConfigurationClassName(String configurationClassName)
1025 {
1026 this.configurationClassName = configurationClassName;
1027 }
1028
1029 /**
1030 * Returns the configuration. This method is called to fetch the
1031 * configuration from the provider. This implementation will call the
1032 * inherited {@link
1033 * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
1034 * createBean()} method to create a new instance of the
1035 * configuration class.
1036 *
1037 * @param decl the bean declaration with initialization parameters for
1038 * the configuration
1039 * @return the new configuration object
1040 * @throws Exception if an error occurs
1041 */
1042 public AbstractConfiguration getConfiguration(
1043 ConfigurationDeclaration decl) throws Exception
1044 {
1045 return (AbstractConfiguration) createBean(fetchConfigurationClass(),
1046 decl, null);
1047 }
1048
1049 /**
1050 * Returns an uninitialized configuration of the represented type. This
1051 * method will be called for optional configurations when the
1052 * {@code getConfiguration()} method caused an error and the
1053 * {@code forceCreate} attribute is set. A concrete sub class can
1054 * here try to create an uninitialized, empty configuration, which may
1055 * be possible if the error was created during initialization. This base
1056 * implementation just returns <b>null</b>.
1057 *
1058 * @param decl the bean declaration with initialization parameters for
1059 * the configuration
1060 * @return the new configuration object
1061 * @throws Exception if an error occurs
1062 * @since 1.4
1063 */
1064 public AbstractConfiguration getEmptyConfiguration(
1065 ConfigurationDeclaration decl) throws Exception
1066 {
1067 return null;
1068 }
1069
1070 /**
1071 * Returns the configuration class supported by this provider. If a
1072 * class object was set, it is returned. Otherwise the method tries to
1073 * resolve the class name.
1074 *
1075 * @return the class of the configuration to be created
1076 * @since 1.4
1077 */
1078 protected synchronized Class<?> fetchConfigurationClass() throws Exception
1079 {
1080 if (getConfigurationClass() == null)
1081 {
1082 setConfigurationClass(loadClass(getConfigurationClassName()));
1083 }
1084 return getConfigurationClass();
1085 }
1086
1087 /**
1088 * Loads the class with the specified name dynamically. If the class's
1089 * name is <b>null</b>, <b>null</b> will also be returned.
1090 *
1091 * @param className the name of the class to be loaded
1092 * @return the class object
1093 * @throws ClassNotFoundException if class loading fails
1094 * @since 1.4
1095 */
1096 protected Class<?> loadClass(String className)
1097 throws ClassNotFoundException
1098 {
1099 return (className != null) ? Class.forName(className, true,
1100 getClass().getClassLoader()) : null;
1101 }
1102 }
1103
1104 /**
1105 * <p>
1106 * A specialized {@code BeanDeclaration} implementation that
1107 * represents the declaration of a configuration source.
1108 * </p>
1109 * <p>
1110 * Instances of this class are able to extract all information about a
1111 * configuration source from the configuration definition file. The
1112 * declaration of a configuration source is very similar to a bean
1113 * declaration processed by {@code XMLBeanDeclaration}. There are
1114 * very few differences, e.g. some reserved attributes like
1115 * {@code optional} and {@code at} and the fact that a bean
1116 * factory is never needed.
1117 * </p>
1118 */
1119 public static class ConfigurationDeclaration extends XMLBeanDeclaration
1120 {
1121 /** Stores a reference to the associated configuration builder. */
1122 private DefaultConfigurationBuilder configurationBuilder;
1123
1124 /**
1125 * Creates a new instance of {@code ConfigurationDeclaration} and
1126 * initializes it.
1127 *
1128 * @param builder the associated configuration builder
1129 * @param config the configuration this declaration is based onto
1130 */
1131 public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
1132 HierarchicalConfiguration config)
1133 {
1134 super(config);
1135 configurationBuilder = builder;
1136 }
1137
1138 /**
1139 * Returns the associated configuration builder.
1140 *
1141 * @return the configuration builder
1142 */
1143 public DefaultConfigurationBuilder getConfigurationBuilder()
1144 {
1145 return configurationBuilder;
1146 }
1147
1148 /**
1149 * Returns the value of the {@code at} attribute.
1150 *
1151 * @return the value of the {@code at} attribute (can be <b>null</b>)
1152 */
1153 public String getAt()
1154 {
1155 String result = this.getConfiguration().getString(ATTR_AT_RES);
1156 return (result == null) ? this.getConfiguration().getString(ATTR_AT)
1157 : result;
1158 }
1159
1160 /**
1161 * Returns a flag whether this is an optional configuration.
1162 *
1163 * @return a flag if this declaration points to an optional
1164 * configuration
1165 */
1166 public boolean isOptional()
1167 {
1168 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
1169 null);
1170 if (value == null)
1171 {
1172 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
1173 Boolean.FALSE);
1174 }
1175 return value.booleanValue();
1176 }
1177
1178 /**
1179 * Returns a flag whether this configuration should always be created
1180 * and added to the resulting combined configuration. This flag is
1181 * evaluated only for optional configurations whose normal creation has
1182 * caused an error. If for such a configuration the
1183 * {@code forceCreate} attribute is set and the corresponding
1184 * configuration provider supports this mode, an empty configuration
1185 * will be created and added to the resulting combined configuration.
1186 *
1187 * @return the value of the {@code forceCreate} attribute
1188 * @since 1.4
1189 */
1190 public boolean isForceCreate()
1191 {
1192 return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
1193 }
1194
1195 /**
1196 * Returns the name of the bean factory. For configuration source
1197 * declarations always a reserved factory is used. This factory's name
1198 * is returned by this implementation.
1199 *
1200 * @return the name of the bean factory
1201 */
1202 @Override
1203 public String getBeanFactoryName()
1204 {
1205 return CONFIG_BEAN_FACTORY_NAME;
1206 }
1207
1208 /**
1209 * Returns the bean's class name. This implementation will always return
1210 * <b>null</b>.
1211 *
1212 * @return the name of the bean's class
1213 */
1214 @Override
1215 public String getBeanClassName()
1216 {
1217 return null;
1218 }
1219
1220 /**
1221 * Checks whether the given node is reserved. This method will take
1222 * further reserved attributes into account
1223 *
1224 * @param nd the node
1225 * @return a flag whether this node is reserved
1226 */
1227 @Override
1228 protected boolean isReservedNode(ConfigurationNode nd)
1229 {
1230 if (super.isReservedNode(nd))
1231 {
1232 return true;
1233 }
1234
1235 return nd.isAttribute()
1236 && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
1237 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
1238 .equals(nd.getName()) && nd.getParentNode()
1239 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
1240 }
1241
1242 /**
1243 * Performs interpolation. This implementation will delegate
1244 * interpolation to the configuration builder, which takes care that the
1245 * currently constructed configuration is taken into account, too.
1246 *
1247 * @param value the value to be interpolated
1248 * @return the interpolated value
1249 */
1250 @Override
1251 protected Object interpolate(Object value)
1252 {
1253 return getConfigurationBuilder().interpolate(value);
1254 }
1255 }
1256
1257 /**
1258 * A specialized {@code BeanFactory} implementation that handles
1259 * configuration declarations. This class will retrieve the correct
1260 * configuration provider and delegate the task of creating the
1261 * configuration to this object.
1262 */
1263 static class ConfigurationBeanFactory implements BeanFactory
1264 {
1265 /** The logger. */
1266 private Log logger = LogFactory.getLog(DefaultConfigurationBuilder.class);
1267
1268 /**
1269 * Creates an instance of a bean class. This implementation expects that
1270 * the passed in bean declaration is a declaration for a configuration.
1271 * It will determine the responsible configuration provider and delegate
1272 * the call to this instance. If creation of the configuration fails
1273 * and the {@code optional} attribute is set, the exception will
1274 * be ignored. If the {@code forceCreate} attribute is set, too,
1275 * the provider is asked to create an empty configuration. A return
1276 * value of <b>null</b> means that no configuration could be created.
1277 *
1278 * @param beanClass the bean class (will be ignored)
1279 * @param data the declaration
1280 * @param param an additional parameter (will be ignored)
1281 * @return the newly created configuration
1282 * @throws Exception if an error occurs
1283 */
1284 public Object createBean(Class<?> beanClass, BeanDeclaration data,
1285 Object param) throws Exception
1286 {
1287 ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1288 String tagName = decl.getNode().getName();
1289 ConfigurationProvider provider = decl.getConfigurationBuilder()
1290 .providerForTag(tagName);
1291 if (provider == null)
1292 {
1293 throw new ConfigurationRuntimeException(
1294 "No ConfigurationProvider registered for tag "
1295 + tagName);
1296 }
1297
1298 try
1299 {
1300 return provider.getConfiguration(decl);
1301 }
1302 catch (Exception ex)
1303 {
1304 // If this is an optional configuration, ignore the exception
1305 if (!decl.isOptional())
1306 {
1307 throw ex;
1308 }
1309 else
1310 {
1311 if (logger.isDebugEnabled())
1312 {
1313 logger.debug("Load failed for optional configuration " + tagName + ": "
1314 + ex.getMessage());
1315 }
1316 // Notify registered error listeners
1317 decl.getConfigurationBuilder().fireError(
1318 EVENT_ERR_LOAD_OPTIONAL,
1319 decl.getConfiguration().getString(ATTR_NAME), null,
1320 ex);
1321
1322 if (decl.isForceCreate())
1323 {
1324 try
1325 {
1326 return provider.getEmptyConfiguration(decl);
1327 }
1328 catch (Exception ex2)
1329 {
1330 // Ignore exception, return null in this case
1331 ;
1332 }
1333 }
1334 return null;
1335 }
1336 }
1337 }
1338
1339 /**
1340 * Returns the default class for this bean factory.
1341 *
1342 * @return the default class
1343 */
1344 public Class<?> getDefaultBeanClass()
1345 {
1346 // Here some valid class must be returned, otherwise BeanHelper
1347 // will complain that the bean's class cannot be determined
1348 return Configuration.class;
1349 }
1350 }
1351
1352 /**
1353 * A specialized provider implementation that deals with file based
1354 * configurations. Ensures that the base path is correctly set and that the
1355 * load() method gets called.
1356 */
1357 public static class FileConfigurationProvider extends ConfigurationProvider
1358 {
1359 /**
1360 * Creates a new instance of {@code FileConfigurationProvider}.
1361 */
1362 public FileConfigurationProvider()
1363 {
1364 super();
1365 }
1366
1367 /**
1368 * Creates a new instance of {@code FileConfigurationProvider}
1369 * and sets the configuration class.
1370 *
1371 * @param configClass the class for the configurations to be created
1372 */
1373 public FileConfigurationProvider(Class<?> configClass)
1374 {
1375 super(configClass);
1376 }
1377
1378 /**
1379 * Creates a new instance of {@code FileConfigurationProvider}
1380 * and sets the configuration class name.
1381 *
1382 * @param configClassName the name of the configuration to be created
1383 * @since 1.4
1384 */
1385 public FileConfigurationProvider(String configClassName)
1386 {
1387 super(configClassName);
1388 }
1389
1390 /**
1391 * Creates the configuration. After that {@code load()} will be
1392 * called. If this configuration is marked as optional, exceptions will
1393 * be ignored.
1394 *
1395 * @param decl the declaration
1396 * @return the new configuration
1397 * @throws Exception if an error occurs
1398 */
1399 @Override
1400 public AbstractConfiguration getConfiguration(
1401 ConfigurationDeclaration decl) throws Exception
1402 {
1403 AbstractConfiguration result = getEmptyConfiguration(decl);
1404 if (result instanceof FileSystemBased)
1405 {
1406 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1407 if (builder.getFileSystem() != null)
1408 {
1409 ((FileSystemBased) result).setFileSystem(builder.getFileSystem());
1410 }
1411 }
1412 ((FileConfiguration) result).load();
1413 return result;
1414 }
1415
1416 /**
1417 * Returns an uninitialized file configuration. This method will be
1418 * called for optional configurations when the
1419 * {@code getConfiguration()} method caused an error and the
1420 * {@code forceCreate} attribute is set. It will create the
1421 * configuration of the represented type, but the {@code load()}
1422 * method won't be called. This way non-existing configuration files can
1423 * be handled gracefully: If loading a the file fails, an empty
1424 * configuration will be created that is already configured with the
1425 * correct file name.
1426 *
1427 * @param decl the bean declaration with initialization parameters for
1428 * the configuration
1429 * @return the new configuration object
1430 * @throws Exception if an error occurs
1431 * @since 1.4
1432 */
1433 @Override
1434 public AbstractConfiguration getEmptyConfiguration(
1435 ConfigurationDeclaration decl) throws Exception
1436 {
1437 AbstractConfiguration config = super.getConfiguration(decl);
1438
1439 /**
1440 * Some wrapper classes may need to pass the EntityResolver to XMLConfigurations
1441 * they construct buy may not be an XMLConfiguration.
1442 */
1443 if (config instanceof EntityResolverSupport)
1444 {
1445 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1446 EntityResolver resolver = builder.getEntityResolver();
1447 ((EntityResolverSupport) config).setEntityResolver(resolver);
1448 }
1449
1450 return config;
1451 }
1452
1453 /**
1454 * Initializes the bean instance. Ensures that the file configuration's
1455 * base path will be initialized with the base path of the factory so
1456 * that relative path names can be correctly resolved.
1457 *
1458 * @param bean the bean to be initialized
1459 * @param data the declaration
1460 * @throws Exception if an error occurs
1461 */
1462 @Override
1463 protected void initBeanInstance(Object bean, BeanDeclaration data)
1464 throws Exception
1465 {
1466 FileConfiguration config = (FileConfiguration) bean;
1467 config.setBasePath(((ConfigurationDeclaration) data)
1468 .getConfigurationBuilder().getConfigurationBasePath());
1469 super.initBeanInstance(bean, data);
1470 }
1471 }
1472
1473 /**
1474 * A specialized configuration provider for XML configurations. This
1475 * implementation acts like a {@code FileConfigurationProvider}, but
1476 * it will copy all entity IDs that have been registered for the
1477 * configuration builder to the new XML configuration before it is loaded.
1478 *
1479 * @since 1.6
1480 */
1481 public static class XMLConfigurationProvider extends FileConfigurationProvider
1482 {
1483 /**
1484 * Creates a new instance of {@code XMLConfigurationProvider}.
1485 */
1486 public XMLConfigurationProvider()
1487 {
1488 super(XMLConfiguration.class);
1489 }
1490
1491 /**
1492 * Returns a new empty configuration instance. This implementation
1493 * performs some additional initialization specific to XML
1494 * configurations.
1495 *
1496 * @param decl the configuration declaration
1497 * @return the new configuration
1498 * @throws Exception if an error occurs
1499 */
1500 @Override
1501 public AbstractConfiguration getEmptyConfiguration(
1502 ConfigurationDeclaration decl) throws Exception
1503 {
1504 XMLConfiguration config = (XMLConfiguration) super
1505 .getEmptyConfiguration(decl);
1506
1507 DefaultConfigurationBuilder builder = decl
1508 .getConfigurationBuilder();
1509 EntityResolver resolver = builder.getEntityResolver();
1510 if (resolver instanceof EntityRegistry)
1511 {
1512 // copy the registered entities
1513 config.getRegisteredEntities().putAll(
1514 builder.getRegisteredEntities());
1515 }
1516 else
1517 {
1518 config.setEntityResolver(resolver);
1519 }
1520 return config;
1521 }
1522 }
1523
1524 /**
1525 * A specialized configuration provider for file based configurations that
1526 * can handle configuration sources whose concrete type depends on the
1527 * extension of the file to be loaded. One example is the
1528 * {@code properties} tag: if the file ends with ".xml" a
1529 * XMLPropertiesConfiguration object must be created, otherwise a
1530 * PropertiesConfiguration object.
1531 */
1532 static class FileExtensionConfigurationProvider extends
1533 FileConfigurationProvider
1534 {
1535 /**
1536 * Stores the class to be created when the file extension matches.
1537 */
1538 private Class<?> matchingClass;
1539
1540 /**
1541 * Stores the name of the class to be created when the file extension
1542 * matches.
1543 */
1544 private String matchingClassName;
1545
1546 /**
1547 * Stores the class to be created when the file extension does not
1548 * match.
1549 */
1550 private Class<?> defaultClass;
1551
1552 /**
1553 * Stores the name of the class to be created when the file extension
1554 * does not match.
1555 */
1556 private String defaultClassName;
1557
1558 /** Stores the file extension to be checked against. */
1559 private String fileExtension;
1560
1561 /**
1562 * Creates a new instance of
1563 * {@code FileExtensionConfigurationProvider} and initializes it.
1564 *
1565 * @param matchingClass the class to be created when the file extension
1566 * matches
1567 * @param defaultClass the class to be created when the file extension
1568 * does not match
1569 * @param extension the file extension to be checked against
1570 */
1571 public FileExtensionConfigurationProvider(Class<?> matchingClass,
1572 Class<?> defaultClass, String extension)
1573 {
1574 this.matchingClass = matchingClass;
1575 this.defaultClass = defaultClass;
1576 fileExtension = extension;
1577 }
1578
1579 /**
1580 * Creates a new instance of
1581 * {@code FileExtensionConfigurationProvider} and initializes it
1582 * with the names of the classes to be created.
1583 *
1584 * @param matchingClassName the name of the class to be created when the
1585 * file extension matches
1586 * @param defaultClassName the name of the class to be created when the
1587 * file extension does not match
1588 * @param extension the file extension to be checked against
1589 * @since 1.4
1590 */
1591 public FileExtensionConfigurationProvider(String matchingClassName,
1592 String defaultClassName, String extension)
1593 {
1594 this.matchingClassName = matchingClassName;
1595 this.defaultClassName = defaultClassName;
1596 fileExtension = extension;
1597 }
1598
1599 /**
1600 * Returns the matching class object, no matter whether it was defined
1601 * as a class or as a class name.
1602 *
1603 * @return the matching class object
1604 * @throws Exception if an error occurs
1605 * @since 1.4
1606 */
1607 protected synchronized Class<?> fetchMatchingClass() throws Exception
1608 {
1609 if (matchingClass == null)
1610 {
1611 matchingClass = loadClass(matchingClassName);
1612 }
1613 return matchingClass;
1614 }
1615
1616 /**
1617 * Returns the default class object, no matter whether it was defined as
1618 * a class or as a class name.
1619 *
1620 * @return the default class object
1621 * @throws Exception if an error occurs
1622 * @since 1.4
1623 */
1624 protected synchronized Class<?> fetchDefaultClass() throws Exception
1625 {
1626 if (defaultClass == null)
1627 {
1628 defaultClass = loadClass(defaultClassName);
1629 }
1630 return defaultClass;
1631 }
1632
1633 /**
1634 * Creates the configuration object. The class is determined by the file
1635 * name's extension.
1636 *
1637 * @param beanClass the class
1638 * @param data the bean declaration
1639 * @return the new bean
1640 * @throws Exception if an error occurs
1641 */
1642 @Override
1643 protected Object createBeanInstance(Class<?> beanClass,
1644 BeanDeclaration data) throws Exception
1645 {
1646 String fileName = ((ConfigurationDeclaration) data)
1647 .getConfiguration().getString(ATTR_FILENAME);
1648 if (fileName != null
1649 && fileName.toLowerCase().trim().endsWith(fileExtension))
1650 {
1651 return super.createBeanInstance(fetchMatchingClass(), data);
1652 }
1653 else
1654 {
1655 return super.createBeanInstance(fetchDefaultClass(), data);
1656 }
1657 }
1658 }
1659
1660 /**
1661 * A specialized configuration provider class that allows to include other
1662 * configuration definition files.
1663 */
1664 static class ConfigurationBuilderProvider extends ConfigurationProvider
1665 {
1666 /**
1667 * Creates a new instance of {@code ConfigurationBuilderProvider}.
1668 */
1669 public ConfigurationBuilderProvider()
1670 {
1671 super(DefaultConfigurationBuilder.class);
1672 }
1673
1674 /**
1675 * Creates the configuration. First creates a configuration builder
1676 * object. Then returns the configuration created by this builder.
1677 *
1678 * @param decl the configuration declaration
1679 * @return the configuration
1680 * @exception Exception if an error occurs
1681 */
1682 @Override
1683 public AbstractConfiguration getConfiguration(
1684 ConfigurationDeclaration decl) throws Exception
1685 {
1686 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
1687 .getConfiguration(decl);
1688 return builder.getConfiguration(true);
1689 }
1690
1691 /**
1692 * Returns an empty configuration in case of an optional configuration
1693 * could not be created. This implementation returns an empty combined
1694 * configuration.
1695 *
1696 * @param decl the configuration declaration
1697 * @return the configuration
1698 * @exception Exception if an error occurs
1699 * @since 1.4
1700 */
1701 @Override
1702 public AbstractConfiguration getEmptyConfiguration(
1703 ConfigurationDeclaration decl) throws Exception
1704 {
1705 return new CombinedConfiguration();
1706 }
1707
1708 /**
1709 * {@inheritDoc} This implementation ensures that the configuration
1710 * builder created by this provider inherits the properties from the
1711 * current configuration builder.
1712 */
1713 @Override
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 (ConfigurationListener l : parent.getConfigurationListeners())
1747 {
1748 child.addConfigurationListener(l);
1749 }
1750 child.clearErrorListeners();
1751 for (ConfigurationErrorListener l : parent.getErrorListeners())
1752 {
1753 child.addErrorListener(l);
1754 }
1755 }
1756 }
1757 }