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