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 1366930 2012-07-29 20:05:36Z 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 /**
418 * A specialized {@code StrLookup} object which operates on the combined
419 * configuration constructed by this builder. This object is used as
420 * default lookup for {@code ConfigurationInterpolator} objects assigned to
421 * newly created configuration objects.
422 */
423 private final StrLookup combinedConfigLookup = new StrLookup()
424 {
425 @Override
426 public String lookup(String key)
427 {
428 if (constructedConfiguration != null)
429 {
430 Object value =
431 constructedConfiguration.resolveContainerStore(key);
432 return (value != null) ? value.toString() : null;
433 }
434 return null;
435 }
436 };
437
438 /** Stores the configuration that is currently constructed.*/
439 private CombinedConfiguration constructedConfiguration;
440
441 /** Stores a map with the registered configuration providers. */
442 private final Map<String, ConfigurationProvider> providers;
443
444 /** Stores the base path to the configuration sources to load. */
445 private String configurationBasePath;
446
447 /**
448 * Creates a new instance of {@code DefaultConfigurationBuilder}. A
449 * configuration definition file is not yet loaded. Use the diverse setter
450 * methods provided by file based configurations to specify the
451 * configuration definition file.
452 */
453 public DefaultConfigurationBuilder()
454 {
455 super();
456 providers = new HashMap<String, ConfigurationProvider>();
457 registerDefaultProviders();
458 registerBeanFactory();
459 setLogger(LogFactory.getLog(getClass()));
460 addErrorLogListener(); // log errors per default
461 }
462
463 /**
464 * Creates a new instance of {@code DefaultConfigurationBuilder} and
465 * sets the specified configuration definition file.
466 *
467 * @param file the configuration definition file
468 */
469 public DefaultConfigurationBuilder(File file)
470 {
471 this();
472 setFile(file);
473 }
474
475 /**
476 * Creates a new instance of {@code DefaultConfigurationBuilder} and
477 * sets the specified configuration definition file.
478 *
479 * @param fileName the name of the configuration definition file
480 * @throws ConfigurationException if an error occurs when the file is loaded
481 */
482 public DefaultConfigurationBuilder(String fileName)
483 throws ConfigurationException
484 {
485 this();
486 setFileName(fileName);
487 }
488
489 /**
490 * Creates a new instance of {@code DefaultConfigurationBuilder} and
491 * sets the specified configuration definition file.
492 *
493 * @param url the URL to the configuration definition file
494 * @throws ConfigurationException if an error occurs when the file is loaded
495 */
496 public DefaultConfigurationBuilder(URL url) throws ConfigurationException
497 {
498 this();
499 setURL(url);
500 }
501
502 /**
503 * Returns the base path for the configuration sources to load. This path is
504 * used to resolve relative paths in the configuration definition file.
505 *
506 * @return the base path for configuration sources
507 */
508 public String getConfigurationBasePath()
509 {
510 return (configurationBasePath != null) ? configurationBasePath
511 : getBasePath();
512 }
513
514 /**
515 * Sets the base path for the configuration sources to load. Normally a base
516 * path need not to be set because it is determined by the location of the
517 * configuration definition file to load. All relative paths in this file
518 * are resolved relative to this file. Setting a base path makes sense if
519 * such relative paths should be otherwise resolved, e.g. if the
520 * configuration file is loaded from the class path and all sub
521 * configurations it refers to are stored in a special config directory.
522 *
523 * @param configurationBasePath the new base path to set
524 */
525 public void setConfigurationBasePath(String configurationBasePath)
526 {
527 this.configurationBasePath = configurationBasePath;
528 }
529
530 /**
531 * Adds a configuration provider for the specified tag. Whenever this tag is
532 * encountered in the configuration definition file this provider will be
533 * called to create the configuration object.
534 *
535 * @param tagName the name of the tag in the configuration definition file
536 * @param provider the provider for this tag
537 */
538 public void addConfigurationProvider(String tagName,
539 ConfigurationProvider provider)
540 {
541 if (tagName == null)
542 {
543 throw new IllegalArgumentException("Tag name must not be null!");
544 }
545 if (provider == null)
546 {
547 throw new IllegalArgumentException("Provider must not be null!");
548 }
549
550 providers.put(tagName, provider);
551 }
552
553 /**
554 * Removes the configuration provider for the specified tag name.
555 *
556 * @param tagName the tag name
557 * @return the removed configuration provider or <b>null</b> if none was
558 * registered for that tag
559 */
560 public ConfigurationProvider removeConfigurationProvider(String tagName)
561 {
562 return providers.remove(tagName);
563 }
564
565 /**
566 * Returns the configuration provider for the given tag.
567 *
568 * @param tagName the name of the tag
569 * @return the provider that was registered for this tag or <b>null</b> if
570 * there is none
571 */
572 public ConfigurationProvider providerForTag(String tagName)
573 {
574 return providers.get(tagName);
575 }
576
577 /**
578 * Returns the configuration provided by this builder. Loads and parses the
579 * configuration definition file and creates instances for the declared
580 * configurations.
581 *
582 * @return the configuration
583 * @throws ConfigurationException if an error occurs
584 */
585 public Configuration getConfiguration() throws ConfigurationException
586 {
587 return getConfiguration(true);
588 }
589
590 /**
591 * Returns the configuration provided by this builder. If the boolean
592 * parameter is <b>true</b>, the configuration definition file will be
593 * loaded. It will then be parsed, and instances for the declared
594 * configurations will be created.
595 *
596 * @param load a flag whether the configuration definition file should be
597 * loaded; a value of <b>false</b> would make sense if the file has already
598 * been created or its content was manipulated using some of the property
599 * accessor methods
600 * @return the configuration
601 * @throws ConfigurationException if an error occurs
602 */
603 public CombinedConfiguration getConfiguration(boolean load)
604 throws ConfigurationException
605 {
606 if (load)
607 {
608 load();
609 }
610
611 initFileSystem();
612 initSystemProperties();
613 configureEntityResolver();
614 registerConfiguredProviders();
615 registerConfiguredLookups();
616
617 CombinedConfiguration result = createResultConfiguration();
618 constructedConfiguration = result;
619
620 List<SubnodeConfiguration> overrides = fetchTopLevelOverrideConfigs();
621 overrides.addAll(fetchChildConfigs(KEY_OVERRIDE));
622 initCombinedConfiguration(result, overrides, KEY_OVERRIDE_LIST);
623
624 List<SubnodeConfiguration> additionals = fetchChildConfigs(KEY_UNION);
625 if (!additionals.isEmpty())
626 {
627 CombinedConfiguration addConfig = createAdditionalsConfiguration(result);
628 result.addConfiguration(addConfig, ADDITIONAL_NAME);
629 initCombinedConfiguration(addConfig, additionals,
630 KEY_ADDITIONAL_LIST);
631 }
632
633 return result;
634 }
635
636 /**
637 * Creates the resulting combined configuration. This method is called by
638 * {@code getConfiguration()}. It checks whether the
639 * {@code header} section of the configuration definition file
640 * contains a {@code result} element. If this is the case, it will be
641 * used to initialize the properties of the newly created configuration
642 * object.
643 *
644 * @return the resulting configuration object
645 * @throws ConfigurationException if an error occurs
646 */
647 protected CombinedConfiguration createResultConfiguration()
648 throws ConfigurationException
649 {
650 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_RESULT, true);
651 CombinedConfiguration result = (CombinedConfiguration) BeanHelper
652 .createBean(decl, CombinedConfiguration.class);
653
654 if (getMaxIndex(KEY_COMBINER) < 0)
655 {
656 // No combiner defined => set default
657 result.setNodeCombiner(new OverrideCombiner());
658 }
659
660 return result;
661 }
662
663 /**
664 * Creates the {@code CombinedConfiguration} for the configuration
665 * sources in the <code><additional></code> section. This method is
666 * called when the builder constructs the final configuration. It creates a
667 * new {@code CombinedConfiguration} and initializes some properties
668 * from the result configuration.
669 *
670 * @param resultConfig the result configuration (this is the configuration
671 * that will be returned by the builder)
672 * @return the {@code CombinedConfiguration} for the additional
673 * configuration sources
674 * @since 1.7
675 */
676 protected CombinedConfiguration createAdditionalsConfiguration(
677 CombinedConfiguration resultConfig)
678 {
679 CombinedConfiguration addConfig =
680 new CombinedConfiguration(new UnionCombiner());
681 addConfig.setDelimiterParsingDisabled(resultConfig
682 .isDelimiterParsingDisabled());
683 addConfig.setForceReloadCheck(resultConfig.isForceReloadCheck());
684 addConfig.setIgnoreReloadExceptions(resultConfig
685 .isIgnoreReloadExceptions());
686 return addConfig;
687 }
688
689 /**
690 * Initializes a combined configuration for the configurations of a specific
691 * section. This method is called for the override and for the additional
692 * section (if it exists).
693 *
694 * @param config the configuration to be initialized
695 * @param containedConfigs the list with the declarations of the contained
696 * configurations
697 * @param keyListNodes a list with the declaration of list nodes
698 * @throws ConfigurationException if an error occurs
699 */
700 protected void initCombinedConfiguration(CombinedConfiguration config,
701 List<? extends HierarchicalConfiguration> containedConfigs,
702 String keyListNodes) throws ConfigurationException
703 {
704 List<Object> listNodes = getList(keyListNodes);
705 for (Object listNode : listNodes)
706 {
707 config.getNodeCombiner().addListNode((String) listNode);
708 }
709
710 for (HierarchicalConfiguration conf : containedConfigs)
711 {
712 ConfigurationDeclaration decl = new ConfigurationDeclaration(this,
713 conf);
714 if (getLogger().isDebugEnabled())
715 {
716 getLogger().debug("Creating configuration " + decl.getBeanClassName() + " with name "
717 + decl.getConfiguration().getString(ATTR_NAME));
718 }
719 AbstractConfiguration newConf = createConfigurationAt(decl);
720 if (newConf != null)
721 {
722 config.addConfiguration(newConf, decl.getConfiguration()
723 .getString(ATTR_NAME), decl.getAt());
724 }
725 }
726 }
727
728 /**
729 * Registers the default configuration providers supported by this class.
730 * This method will be called during initialization. It registers
731 * configuration providers for the tags that are supported by default.
732 */
733 protected void registerDefaultProviders()
734 {
735 for (int i = 0; i < DEFAULT_TAGS.length; i++)
736 {
737 addConfigurationProvider(DEFAULT_TAGS[i], DEFAULT_PROVIDERS[i]);
738 }
739 }
740
741 /**
742 * Registers providers defined in the configuration.
743 *
744 * @throws ConfigurationException if an error occurs
745 */
746 protected void registerConfiguredProviders() throws ConfigurationException
747 {
748 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_PROVIDERS);
749 for (HierarchicalConfiguration config : nodes)
750 {
751 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
752 String key = config.getString(KEY_PROVIDER_KEY);
753 addConfigurationProvider(key, (ConfigurationProvider) BeanHelper
754 .createBean(decl));
755 }
756 }
757
758 /**
759 * Registers StrLookups defined in the configuration.
760 *
761 * @throws ConfigurationException if an error occurs
762 */
763 protected void registerConfiguredLookups() throws ConfigurationException
764 {
765 List<HierarchicalConfiguration> nodes = configurationsAt(KEY_CONFIGURATION_LOOKUPS);
766 for (HierarchicalConfiguration config : nodes)
767 {
768 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
769 String key = config.getString(KEY_LOOKUP_KEY);
770 StrLookup lookup = (StrLookup) BeanHelper.createBean(decl);
771 BeanHelper.setProperty(lookup, "configuration", this);
772 ConfigurationInterpolator.registerGlobalLookup(key, lookup);
773 this.getInterpolator().registerLookup(key, lookup);
774 }
775 }
776
777 protected void initFileSystem() throws ConfigurationException
778 {
779 if (getMaxIndex(FILE_SYSTEM) == 0)
780 {
781 HierarchicalConfiguration config = configurationAt(FILE_SYSTEM);
782 XMLBeanDeclaration decl = new XMLBeanDeclaration(config);
783 setFileSystem((FileSystem) BeanHelper.createBean(decl));
784 }
785 }
786
787 /**
788 * If a property file is configured add the properties to the System properties.
789 * @throws ConfigurationException if an error occurs.
790 */
791 protected void initSystemProperties() throws ConfigurationException
792 {
793 String fileName = getString(KEY_SYSTEM_PROPS);
794 if (fileName != null)
795 {
796 try
797 {
798 SystemConfiguration.setSystemProperties(getConfigurationBasePath(), fileName);
799 }
800 catch (Exception ex)
801 {
802 throw new ConfigurationException("Error setting system properties from " + fileName, ex);
803 }
804
805 }
806 }
807
808 protected void configureEntityResolver() throws ConfigurationException
809 {
810 if (getMaxIndex(KEY_ENTITY_RESOLVER) == 0)
811 {
812 XMLBeanDeclaration decl = new XMLBeanDeclaration(this, KEY_ENTITY_RESOLVER, true);
813 EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class);
814 BeanHelper.setProperty(resolver, "fileSystem", getFileSystem());
815 BeanHelper.setProperty(resolver, "baseDir", getBasePath());
816 BeanHelper.setProperty(resolver, "substitutor", getSubstitutor());
817 setEntityResolver(resolver);
818 }
819 }
820
821 /**
822 * Performs interpolation. This method will not only take this configuration
823 * instance into account (which is the one that loaded the configuration
824 * definition file), but also the so far constructed combined configuration.
825 * So variables can be used that point to properties that are defined in
826 * configuration sources loaded by this builder.
827 *
828 * @param value the value to be interpolated
829 * @return the interpolated value
830 */
831 @Override
832 protected Object interpolate(Object value)
833 {
834 Object result = super.interpolate(value);
835 if (constructedConfiguration != null)
836 {
837 result = constructedConfiguration.interpolate(result);
838 }
839 return result;
840 }
841
842 /**
843 * Creates a configuration object from the specified configuration
844 * declaration.
845 *
846 * @param decl the configuration declaration
847 * @return the new configuration object
848 * @throws ConfigurationException if an error occurs
849 */
850 private AbstractConfiguration createConfigurationAt(
851 ConfigurationDeclaration decl) throws ConfigurationException
852 {
853 try
854 {
855 return (AbstractConfiguration) BeanHelper.createBean(decl);
856 }
857 catch (Exception ex)
858 {
859 // redirect to configuration exceptions
860 throw new ConfigurationException(ex);
861 }
862 }
863
864 /**
865 * Returns a list with {@code SubnodeConfiguration} objects for the
866 * child nodes of the specified configuration node.
867 *
868 * @param node the start node
869 * @return a list with subnode configurations for the node's children
870 */
871 private List<SubnodeConfiguration> fetchChildConfigs(ConfigurationNode node)
872 {
873 List<ConfigurationNode> children = node.getChildren();
874 List<SubnodeConfiguration> result = new ArrayList<SubnodeConfiguration>(children.size());
875 for (ConfigurationNode child : children)
876 {
877 result.add(createSubnodeConfiguration(child));
878 }
879 return result;
880 }
881
882 /**
883 * Returns a list with {@code SubnodeConfiguration} objects for the
884 * child nodes of the node specified by the given key.
885 *
886 * @param key the key (must define exactly one node)
887 * @return a list with subnode configurations for the node's children
888 */
889 private List<SubnodeConfiguration> fetchChildConfigs(String key)
890 {
891 List<ConfigurationNode> nodes = fetchNodeList(key);
892 if (nodes.size() > 0)
893 {
894 return fetchChildConfigs(nodes.get(0));
895 }
896 else
897 {
898 return Collections.emptyList();
899 }
900 }
901
902 /**
903 * Finds the override configurations that are defined as top level elements
904 * in the configuration definition file. This method will fetch the child
905 * elements of the root node and remove the nodes that represent other
906 * configuration sections. The remaining nodes are treated as definitions
907 * for override configurations.
908 *
909 * @return a list with subnode configurations for the top level override
910 * configurations
911 */
912 private List<SubnodeConfiguration> fetchTopLevelOverrideConfigs()
913 {
914 List<SubnodeConfiguration> configs = fetchChildConfigs(getRootNode());
915 for (Iterator<SubnodeConfiguration> it = configs.iterator(); it.hasNext();)
916 {
917 String nodeName = it.next().getRootNode().getName();
918 for (int i = 0; i < CONFIG_SECTIONS.length; i++)
919 {
920 if (CONFIG_SECTIONS[i].equals(nodeName))
921 {
922 it.remove();
923 break;
924 }
925 }
926 }
927 return configs;
928 }
929
930 /**
931 * Registers the bean factory used by this class if necessary. This method
932 * is called by the constructor to ensure that the required bean factory is
933 * available.
934 */
935 private void registerBeanFactory()
936 {
937 synchronized (DefaultConfigurationBuilder.class)
938 {
939 if (!BeanHelper.registeredFactoryNames().contains(
940 CONFIG_BEAN_FACTORY_NAME))
941 {
942 BeanHelper.registerBeanFactory(CONFIG_BEAN_FACTORY_NAME,
943 new ConfigurationBeanFactory());
944 }
945 }
946 }
947
948 /**
949 * <p>
950 * A base class for creating and initializing configuration sources.
951 * </p>
952 * <p>
953 * Concrete sub classes of this base class are responsible for creating
954 * specific {@code Configuration} objects for the tags in the
955 * configuration definition file. The configuration factory will parse the
956 * definition file and try to find a matching
957 * {@code ConfigurationProvider} for each encountered tag. This
958 * provider is then asked to create a corresponding
959 * {@code Configuration} object. It is up to a concrete
960 * implementation how this object is created and initialized.
961 * </p>
962 * <p>
963 * Note that at the moment only configuration classes derived from
964 * {@link AbstractConfiguration} are supported.
965 * </p>
966 */
967 public static class ConfigurationProvider extends DefaultBeanFactory
968 {
969 /** Stores the class of the configuration to be created. */
970 private Class<?> configurationClass;
971
972 /** Stores the name of the configuration class to be created.*/
973 private String configurationClassName;
974
975 /**
976 * Creates a new uninitialized instance of {@code ConfigurationProvider}.
977 */
978 public ConfigurationProvider()
979 {
980 this((Class<?>) null);
981 }
982
983 /**
984 * Creates a new instance of {@code ConfigurationProvider} and
985 * sets the class of the configuration created by this provider.
986 *
987 * @param configClass the configuration class
988 */
989 public ConfigurationProvider(Class<?> configClass)
990 {
991 setConfigurationClass(configClass);
992 }
993
994 /**
995 * Creates a new instance of {@code ConfigurationProvider} and
996 * sets the name of the class of the configuration created by this
997 * provider.
998 *
999 * @param configClassName the name of the configuration class
1000 * @since 1.4
1001 */
1002 public ConfigurationProvider(String configClassName)
1003 {
1004 setConfigurationClassName(configClassName);
1005 }
1006
1007 /**
1008 * Returns the class of the configuration returned by this provider.
1009 *
1010 * @return the class of the provided configuration
1011 */
1012 public Class<?> getConfigurationClass()
1013 {
1014 return configurationClass;
1015 }
1016
1017 /**
1018 * Sets the class of the configuration returned by this provider.
1019 *
1020 * @param configurationClass the configuration class
1021 */
1022 public void setConfigurationClass(Class<?> configurationClass)
1023 {
1024 this.configurationClass = configurationClass;
1025 }
1026
1027 /**
1028 * Returns the name of the configuration class returned by this
1029 * provider.
1030 *
1031 * @return the configuration class name
1032 * @since 1.4
1033 */
1034 public String getConfigurationClassName()
1035 {
1036 return configurationClassName;
1037 }
1038
1039 /**
1040 * Sets the name of the configuration class returned by this provider.
1041 *
1042 * @param configurationClassName the name of the configuration class
1043 * @since 1.4
1044 */
1045 public void setConfigurationClassName(String configurationClassName)
1046 {
1047 this.configurationClassName = configurationClassName;
1048 }
1049
1050 /**
1051 * Returns the configuration. This method is called to fetch the
1052 * configuration from the provider. This implementation will call the
1053 * inherited {@link
1054 * org.apache.commons.configuration.beanutils.DefaultBeanFactory#createBean(Class, BeanDeclaration, Object)
1055 * createBean()} method to create a new instance of the
1056 * configuration class.
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 */
1063 public AbstractConfiguration getConfiguration(
1064 ConfigurationDeclaration decl) throws Exception
1065 {
1066 return (AbstractConfiguration) createBean(fetchConfigurationClass(),
1067 decl, null);
1068 }
1069
1070 /**
1071 * Returns an uninitialized configuration of the represented type. This
1072 * method will be called for optional configurations when the
1073 * {@code getConfiguration()} method caused an error and the
1074 * {@code forceCreate} attribute is set. A concrete sub class can
1075 * here try to create an uninitialized, empty configuration, which may
1076 * be possible if the error was created during initialization. This base
1077 * implementation just returns <b>null</b>.
1078 *
1079 * @param decl the bean declaration with initialization parameters for
1080 * the configuration
1081 * @return the new configuration object
1082 * @throws Exception if an error occurs
1083 * @since 1.4
1084 */
1085 public AbstractConfiguration getEmptyConfiguration(
1086 ConfigurationDeclaration decl) throws Exception
1087 {
1088 return null;
1089 }
1090
1091 /**
1092 * Returns the configuration class supported by this provider. If a
1093 * class object was set, it is returned. Otherwise the method tries to
1094 * resolve the class name.
1095 *
1096 * @return the class of the configuration to be created
1097 * @since 1.4
1098 */
1099 protected synchronized Class<?> fetchConfigurationClass() throws Exception
1100 {
1101 if (getConfigurationClass() == null)
1102 {
1103 setConfigurationClass(loadClass(getConfigurationClassName()));
1104 }
1105 return getConfigurationClass();
1106 }
1107
1108 /**
1109 * Loads the class with the specified name dynamically. If the class's
1110 * name is <b>null</b>, <b>null</b> will also be returned.
1111 *
1112 * @param className the name of the class to be loaded
1113 * @return the class object
1114 * @throws ClassNotFoundException if class loading fails
1115 * @since 1.4
1116 */
1117 protected Class<?> loadClass(String className)
1118 throws ClassNotFoundException
1119 {
1120 return (className != null) ? Class.forName(className, true,
1121 getClass().getClassLoader()) : null;
1122 }
1123 }
1124
1125 /**
1126 * <p>
1127 * A specialized {@code BeanDeclaration} implementation that
1128 * represents the declaration of a configuration source.
1129 * </p>
1130 * <p>
1131 * Instances of this class are able to extract all information about a
1132 * configuration source from the configuration definition file. The
1133 * declaration of a configuration source is very similar to a bean
1134 * declaration processed by {@code XMLBeanDeclaration}. There are
1135 * very few differences, e.g. some reserved attributes like
1136 * {@code optional} and {@code at} and the fact that a bean
1137 * factory is never needed.
1138 * </p>
1139 */
1140 public static class ConfigurationDeclaration extends XMLBeanDeclaration
1141 {
1142 /** Stores a reference to the associated configuration builder. */
1143 private DefaultConfigurationBuilder configurationBuilder;
1144
1145 /**
1146 * Creates a new instance of {@code ConfigurationDeclaration} and
1147 * initializes it.
1148 *
1149 * @param builder the associated configuration builder
1150 * @param config the configuration this declaration is based onto
1151 */
1152 public ConfigurationDeclaration(DefaultConfigurationBuilder builder,
1153 HierarchicalConfiguration config)
1154 {
1155 super(config);
1156 configurationBuilder = builder;
1157 }
1158
1159 /**
1160 * Returns the associated configuration builder.
1161 *
1162 * @return the configuration builder
1163 */
1164 public DefaultConfigurationBuilder getConfigurationBuilder()
1165 {
1166 return configurationBuilder;
1167 }
1168
1169 /**
1170 * Returns the value of the {@code at} attribute.
1171 *
1172 * @return the value of the {@code at} attribute (can be <b>null</b>)
1173 */
1174 public String getAt()
1175 {
1176 String result = this.getConfiguration().getString(ATTR_AT_RES);
1177 return (result == null) ? this.getConfiguration().getString(ATTR_AT)
1178 : result;
1179 }
1180
1181 /**
1182 * Returns a flag whether this is an optional configuration.
1183 *
1184 * @return a flag if this declaration points to an optional
1185 * configuration
1186 */
1187 public boolean isOptional()
1188 {
1189 Boolean value = this.getConfiguration().getBoolean(ATTR_OPTIONAL_RES,
1190 null);
1191 if (value == null)
1192 {
1193 value = this.getConfiguration().getBoolean(ATTR_OPTIONAL,
1194 Boolean.FALSE);
1195 }
1196 return value.booleanValue();
1197 }
1198
1199 /**
1200 * Returns a flag whether this configuration should always be created
1201 * and added to the resulting combined configuration. This flag is
1202 * evaluated only for optional configurations whose normal creation has
1203 * caused an error. If for such a configuration the
1204 * {@code forceCreate} attribute is set and the corresponding
1205 * configuration provider supports this mode, an empty configuration
1206 * will be created and added to the resulting combined configuration.
1207 *
1208 * @return the value of the {@code forceCreate} attribute
1209 * @since 1.4
1210 */
1211 public boolean isForceCreate()
1212 {
1213 return this.getConfiguration().getBoolean(ATTR_FORCECREATE, false);
1214 }
1215
1216 /**
1217 * Returns the name of the bean factory. For configuration source
1218 * declarations always a reserved factory is used. This factory's name
1219 * is returned by this implementation.
1220 *
1221 * @return the name of the bean factory
1222 */
1223 @Override
1224 public String getBeanFactoryName()
1225 {
1226 return CONFIG_BEAN_FACTORY_NAME;
1227 }
1228
1229 /**
1230 * Returns the bean's class name. This implementation will always return
1231 * <b>null</b>.
1232 *
1233 * @return the name of the bean's class
1234 */
1235 @Override
1236 public String getBeanClassName()
1237 {
1238 return null;
1239 }
1240
1241 /**
1242 * Checks whether the given node is reserved. This method will take
1243 * further reserved attributes into account
1244 *
1245 * @param nd the node
1246 * @return a flag whether this node is reserved
1247 */
1248 @Override
1249 protected boolean isReservedNode(ConfigurationNode nd)
1250 {
1251 if (super.isReservedNode(nd))
1252 {
1253 return true;
1254 }
1255
1256 return nd.isAttribute()
1257 && ((ATTR_ATNAME.equals(nd.getName()) && nd.getParentNode()
1258 .getAttributeCount(RESERVED_PREFIX + ATTR_ATNAME) == 0) || (ATTR_OPTIONALNAME
1259 .equals(nd.getName()) && nd.getParentNode()
1260 .getAttributeCount(RESERVED_PREFIX + ATTR_OPTIONALNAME) == 0));
1261 }
1262
1263 /**
1264 * Performs interpolation. This implementation will delegate
1265 * interpolation to the configuration builder, which takes care that the
1266 * currently constructed configuration is taken into account, too.
1267 *
1268 * @param value the value to be interpolated
1269 * @return the interpolated value
1270 */
1271 @Override
1272 protected Object interpolate(Object value)
1273 {
1274 return getConfigurationBuilder().interpolate(value);
1275 }
1276 }
1277
1278 /**
1279 * A specialized {@code BeanFactory} implementation that handles
1280 * configuration declarations. This class will retrieve the correct
1281 * configuration provider and delegate the task of creating the
1282 * configuration to this object.
1283 */
1284 static class ConfigurationBeanFactory implements BeanFactory
1285 {
1286 /** The logger. */
1287 private Log logger = LogFactory.getLog(DefaultConfigurationBuilder.class);
1288
1289 /**
1290 * Creates an instance of a bean class. This implementation expects that
1291 * the passed in bean declaration is a declaration for a configuration.
1292 * It will determine the responsible configuration provider and delegate
1293 * the call to this instance. If creation of the configuration fails
1294 * and the {@code optional} attribute is set, the exception will
1295 * be ignored. If the {@code forceCreate} attribute is set, too,
1296 * the provider is asked to create an empty configuration. A return
1297 * value of <b>null</b> means that no configuration could be created.
1298 *
1299 * @param beanClass the bean class (will be ignored)
1300 * @param data the declaration
1301 * @param param an additional parameter (will be ignored)
1302 * @return the newly created configuration
1303 * @throws Exception if an error occurs
1304 */
1305 public Object createBean(Class<?> beanClass, BeanDeclaration data,
1306 Object param) throws Exception
1307 {
1308 ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1309 String tagName = decl.getNode().getName();
1310 ConfigurationProvider provider = decl.getConfigurationBuilder()
1311 .providerForTag(tagName);
1312 if (provider == null)
1313 {
1314 throw new ConfigurationRuntimeException(
1315 "No ConfigurationProvider registered for tag "
1316 + tagName);
1317 }
1318
1319 try
1320 {
1321 AbstractConfiguration config = provider.getConfiguration(decl);
1322 installInterpolator(decl, config);
1323 return config;
1324 }
1325 catch (Exception ex)
1326 {
1327 // If this is an optional configuration, ignore the exception
1328 if (!decl.isOptional())
1329 {
1330 throw ex;
1331 }
1332 else
1333 {
1334 if (logger.isDebugEnabled())
1335 {
1336 logger.debug("Load failed for optional configuration " + tagName + ": "
1337 + ex.getMessage());
1338 }
1339 // Notify registered error listeners
1340 decl.getConfigurationBuilder().fireError(
1341 EVENT_ERR_LOAD_OPTIONAL,
1342 decl.getConfiguration().getString(ATTR_NAME), null,
1343 ex);
1344
1345 if (decl.isForceCreate())
1346 {
1347 try
1348 {
1349 return provider.getEmptyConfiguration(decl);
1350 }
1351 catch (Exception ex2)
1352 {
1353 // Ignore exception, return null in this case
1354 logger.warn("Could not create instance of optional configuration "
1355 + tagName, ex2);
1356 }
1357 }
1358 return null;
1359 }
1360 }
1361 }
1362
1363 /**
1364 * Returns the default class for this bean factory.
1365 *
1366 * @return the default class
1367 */
1368 public Class<?> getDefaultBeanClass()
1369 {
1370 // Here some valid class must be returned, otherwise BeanHelper
1371 // will complain that the bean's class cannot be determined
1372 return Configuration.class;
1373 }
1374
1375 /**
1376 * Installs a specialized {@code ConfigurationInterpolator} on a newly
1377 * created configuration which also takes the combined configuration
1378 * created by the builder into account. With this
1379 * {@code ConfigurationInterpolator} the interpolation facilities of
1380 * this child configuration are extended to include all other
1381 * configurations created by this builder.
1382 *
1383 * @param decl the {@code ConfigurationDeclaration}
1384 * @param config the newly created configuration instance
1385 */
1386 private void installInterpolator(ConfigurationDeclaration decl,
1387 AbstractConfiguration config)
1388 {
1389 ConfigurationInterpolator parent = new ConfigurationInterpolator();
1390 parent.setDefaultLookup(decl.getConfigurationBuilder().combinedConfigLookup);
1391 config.getInterpolator().setParentInterpolator(parent);
1392 }
1393 }
1394
1395 /**
1396 * A specialized provider implementation that deals with file based
1397 * configurations. Ensures that the base path is correctly set and that the
1398 * load() method gets called.
1399 */
1400 public static class FileConfigurationProvider extends ConfigurationProvider
1401 {
1402 /**
1403 * Creates a new instance of {@code FileConfigurationProvider}.
1404 */
1405 public FileConfigurationProvider()
1406 {
1407 super();
1408 }
1409
1410 /**
1411 * Creates a new instance of {@code FileConfigurationProvider}
1412 * and sets the configuration class.
1413 *
1414 * @param configClass the class for the configurations to be created
1415 */
1416 public FileConfigurationProvider(Class<?> configClass)
1417 {
1418 super(configClass);
1419 }
1420
1421 /**
1422 * Creates a new instance of {@code FileConfigurationProvider}
1423 * and sets the configuration class name.
1424 *
1425 * @param configClassName the name of the configuration to be created
1426 * @since 1.4
1427 */
1428 public FileConfigurationProvider(String configClassName)
1429 {
1430 super(configClassName);
1431 }
1432
1433 /**
1434 * Creates the configuration. After that {@code load()} will be
1435 * called. If this configuration is marked as optional, exceptions will
1436 * be ignored.
1437 *
1438 * @param decl the declaration
1439 * @return the new configuration
1440 * @throws Exception if an error occurs
1441 */
1442 @Override
1443 public AbstractConfiguration getConfiguration(
1444 ConfigurationDeclaration decl) throws Exception
1445 {
1446 AbstractConfiguration result = getEmptyConfiguration(decl);
1447 if (result instanceof FileSystemBased)
1448 {
1449 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1450 if (builder.getFileSystem() != null)
1451 {
1452 ((FileSystemBased) result).setFileSystem(builder.getFileSystem());
1453 }
1454 }
1455 ((FileConfiguration) result).load();
1456 return result;
1457 }
1458
1459 /**
1460 * Returns an uninitialized file configuration. This method will be
1461 * called for optional configurations when the
1462 * {@code getConfiguration()} method caused an error and the
1463 * {@code forceCreate} attribute is set. It will create the
1464 * configuration of the represented type, but the {@code load()}
1465 * method won't be called. This way non-existing configuration files can
1466 * be handled gracefully: If loading a the file fails, an empty
1467 * configuration will be created that is already configured with the
1468 * correct file name.
1469 *
1470 * @param decl the bean declaration with initialization parameters for
1471 * the configuration
1472 * @return the new configuration object
1473 * @throws Exception if an error occurs
1474 * @since 1.4
1475 */
1476 @Override
1477 public AbstractConfiguration getEmptyConfiguration(
1478 ConfigurationDeclaration decl) throws Exception
1479 {
1480 AbstractConfiguration config = super.getConfiguration(decl);
1481
1482 /**
1483 * Some wrapper classes may need to pass the EntityResolver to XMLConfigurations
1484 * they construct buy may not be an XMLConfiguration.
1485 */
1486 if (config instanceof EntityResolverSupport)
1487 {
1488 DefaultConfigurationBuilder builder = decl.getConfigurationBuilder();
1489 EntityResolver resolver = builder.getEntityResolver();
1490 ((EntityResolverSupport) config).setEntityResolver(resolver);
1491 }
1492
1493 return config;
1494 }
1495
1496 /**
1497 * Initializes the bean instance. Ensures that the file configuration's
1498 * base path will be initialized with the base path of the factory so
1499 * that relative path names can be correctly resolved.
1500 *
1501 * @param bean the bean to be initialized
1502 * @param data the declaration
1503 * @throws Exception if an error occurs
1504 */
1505 @Override
1506 protected void initBeanInstance(Object bean, BeanDeclaration data)
1507 throws Exception
1508 {
1509 FileConfiguration config = (FileConfiguration) bean;
1510 config.setBasePath(((ConfigurationDeclaration) data)
1511 .getConfigurationBuilder().getConfigurationBasePath());
1512 super.initBeanInstance(bean, data);
1513 }
1514 }
1515
1516 /**
1517 * A specialized configuration provider for XML configurations. This
1518 * implementation acts like a {@code FileConfigurationProvider}, but
1519 * it will copy all entity IDs that have been registered for the
1520 * configuration builder to the new XML configuration before it is loaded.
1521 *
1522 * @since 1.6
1523 */
1524 public static class XMLConfigurationProvider extends FileConfigurationProvider
1525 {
1526 /**
1527 * Creates a new instance of {@code XMLConfigurationProvider}.
1528 */
1529 public XMLConfigurationProvider()
1530 {
1531 super(XMLConfiguration.class);
1532 }
1533
1534 /**
1535 * Returns a new empty configuration instance. This implementation
1536 * performs some additional initialization specific to XML
1537 * configurations.
1538 *
1539 * @param decl the configuration declaration
1540 * @return the new configuration
1541 * @throws Exception if an error occurs
1542 */
1543 @Override
1544 public AbstractConfiguration getEmptyConfiguration(
1545 ConfigurationDeclaration decl) throws Exception
1546 {
1547 XMLConfiguration config = (XMLConfiguration) super
1548 .getEmptyConfiguration(decl);
1549
1550 DefaultConfigurationBuilder builder = decl
1551 .getConfigurationBuilder();
1552 EntityResolver resolver = builder.getEntityResolver();
1553 if (resolver instanceof EntityRegistry)
1554 {
1555 // copy the registered entities
1556 config.getRegisteredEntities().putAll(
1557 builder.getRegisteredEntities());
1558 }
1559 else
1560 {
1561 config.setEntityResolver(resolver);
1562 }
1563 return config;
1564 }
1565 }
1566
1567 /**
1568 * A specialized configuration provider for file based configurations that
1569 * can handle configuration sources whose concrete type depends on the
1570 * extension of the file to be loaded. One example is the
1571 * {@code properties} tag: if the file ends with ".xml" a
1572 * XMLPropertiesConfiguration object must be created, otherwise a
1573 * PropertiesConfiguration object.
1574 */
1575 static class FileExtensionConfigurationProvider extends
1576 FileConfigurationProvider
1577 {
1578 /**
1579 * Stores the class to be created when the file extension matches.
1580 */
1581 private Class<?> matchingClass;
1582
1583 /**
1584 * Stores the name of the class to be created when the file extension
1585 * matches.
1586 */
1587 private String matchingClassName;
1588
1589 /**
1590 * Stores the class to be created when the file extension does not
1591 * match.
1592 */
1593 private Class<?> defaultClass;
1594
1595 /**
1596 * Stores the name of the class to be created when the file extension
1597 * does not match.
1598 */
1599 private String defaultClassName;
1600
1601 /** Stores the file extension to be checked against. */
1602 private String fileExtension;
1603
1604 /**
1605 * Creates a new instance of
1606 * {@code FileExtensionConfigurationProvider} and initializes it.
1607 *
1608 * @param matchingClass the class to be created when the file extension
1609 * matches
1610 * @param defaultClass the class to be created when the file extension
1611 * does not match
1612 * @param extension the file extension to be checked against
1613 */
1614 public FileExtensionConfigurationProvider(Class<?> matchingClass,
1615 Class<?> defaultClass, String extension)
1616 {
1617 this.matchingClass = matchingClass;
1618 this.defaultClass = defaultClass;
1619 fileExtension = extension;
1620 }
1621
1622 /**
1623 * Creates a new instance of
1624 * {@code FileExtensionConfigurationProvider} and initializes it
1625 * with the names of the classes to be created.
1626 *
1627 * @param matchingClassName the name of the class to be created when the
1628 * file extension matches
1629 * @param defaultClassName the name of the class to be created when the
1630 * file extension does not match
1631 * @param extension the file extension to be checked against
1632 * @since 1.4
1633 */
1634 public FileExtensionConfigurationProvider(String matchingClassName,
1635 String defaultClassName, String extension)
1636 {
1637 this.matchingClassName = matchingClassName;
1638 this.defaultClassName = defaultClassName;
1639 fileExtension = extension;
1640 }
1641
1642 /**
1643 * Returns the matching class object, no matter whether it was defined
1644 * as a class or as a class name.
1645 *
1646 * @return the matching class object
1647 * @throws Exception if an error occurs
1648 * @since 1.4
1649 */
1650 protected synchronized Class<?> fetchMatchingClass() throws Exception
1651 {
1652 if (matchingClass == null)
1653 {
1654 matchingClass = loadClass(matchingClassName);
1655 }
1656 return matchingClass;
1657 }
1658
1659 /**
1660 * Returns the default class object, no matter whether it was defined as
1661 * a class or as a class name.
1662 *
1663 * @return the default class object
1664 * @throws Exception if an error occurs
1665 * @since 1.4
1666 */
1667 protected synchronized Class<?> fetchDefaultClass() throws Exception
1668 {
1669 if (defaultClass == null)
1670 {
1671 defaultClass = loadClass(defaultClassName);
1672 }
1673 return defaultClass;
1674 }
1675
1676 /**
1677 * Creates the configuration object. The class is determined by the file
1678 * name's extension.
1679 *
1680 * @param beanClass the class
1681 * @param data the bean declaration
1682 * @return the new bean
1683 * @throws Exception if an error occurs
1684 */
1685 @Override
1686 protected Object createBeanInstance(Class<?> beanClass,
1687 BeanDeclaration data) throws Exception
1688 {
1689 String fileName = ((ConfigurationDeclaration) data)
1690 .getConfiguration().getString(ATTR_FILENAME);
1691 if (fileName != null
1692 && fileName.toLowerCase().trim().endsWith(fileExtension))
1693 {
1694 return super.createBeanInstance(fetchMatchingClass(), data);
1695 }
1696 else
1697 {
1698 return super.createBeanInstance(fetchDefaultClass(), data);
1699 }
1700 }
1701 }
1702
1703 /**
1704 * A specialized configuration provider class that allows to include other
1705 * configuration definition files.
1706 */
1707 static class ConfigurationBuilderProvider extends ConfigurationProvider
1708 {
1709 /**
1710 * Creates a new instance of {@code ConfigurationBuilderProvider}.
1711 */
1712 public ConfigurationBuilderProvider()
1713 {
1714 super(DefaultConfigurationBuilder.class);
1715 }
1716
1717 /**
1718 * Creates the configuration. First creates a configuration builder
1719 * object. Then returns the configuration created by this builder.
1720 *
1721 * @param decl the configuration declaration
1722 * @return the configuration
1723 * @exception Exception if an error occurs
1724 */
1725 @Override
1726 public AbstractConfiguration getConfiguration(
1727 ConfigurationDeclaration decl) throws Exception
1728 {
1729 DefaultConfigurationBuilder builder = (DefaultConfigurationBuilder) super
1730 .getConfiguration(decl);
1731 return builder.getConfiguration(true);
1732 }
1733
1734 /**
1735 * Returns an empty configuration in case of an optional configuration
1736 * could not be created. This implementation returns an empty combined
1737 * configuration.
1738 *
1739 * @param decl the configuration declaration
1740 * @return the configuration
1741 * @exception Exception if an error occurs
1742 * @since 1.4
1743 */
1744 @Override
1745 public AbstractConfiguration getEmptyConfiguration(
1746 ConfigurationDeclaration decl) throws Exception
1747 {
1748 return new CombinedConfiguration();
1749 }
1750
1751 /**
1752 * {@inheritDoc} This implementation ensures that the configuration
1753 * builder created by this provider inherits the properties from the
1754 * current configuration builder.
1755 */
1756 @Override
1757 protected void initBeanInstance(Object bean, BeanDeclaration data)
1758 throws Exception
1759 {
1760 ConfigurationDeclaration decl = (ConfigurationDeclaration) data;
1761 initChildBuilder(decl.getConfigurationBuilder(),
1762 (DefaultConfigurationBuilder) bean);
1763 super.initBeanInstance(bean, data);
1764 }
1765
1766 /**
1767 * Initializes the given child configuration builder from its parent
1768 * builder. This method copies the values of some properties from the
1769 * parent builder to the child builder so that the child inherits
1770 * properties from its parent.
1771 *
1772 * @param parent the parent builder
1773 * @param child the child builder
1774 */
1775 private static void initChildBuilder(
1776 DefaultConfigurationBuilder parent,
1777 DefaultConfigurationBuilder child)
1778 {
1779 child.setAttributeSplittingDisabled(parent
1780 .isAttributeSplittingDisabled());
1781 child.setBasePath(parent.getBasePath());
1782 child.setDelimiterParsingDisabled(parent
1783 .isDelimiterParsingDisabled());
1784 child.setListDelimiter(parent.getListDelimiter());
1785 child.setThrowExceptionOnMissing(parent.isThrowExceptionOnMissing());
1786 child.setLogger(parent.getLogger());
1787
1788 child.clearConfigurationListeners();
1789 for (ConfigurationListener l : parent.getConfigurationListeners())
1790 {
1791 child.addConfigurationListener(l);
1792 }
1793 child.clearErrorListeners();
1794 for (ConfigurationErrorListener l : parent.getErrorListeners())
1795 {
1796 child.addErrorListener(l);
1797 }
1798 }
1799 }
1800 }