Configuration Framework
How it was done
Traditionally, Pustefix used simple Java properties for configuration. These properties were simply passed to all components needing configuration, so that they could read the properties needed to configure themselves. As writing large property files can be quite awkward, some XSL templates were provided, which could be used to build these property files automatically from XML files with a custom configuration syntax.
How it is done now
This approach proofed to have two major problems: On one hand configuration files had to be regenerated manually whenever the configuration was changed. On the other hand keeping track of all the properties used in the framework became more and more difficult while the framework was growing.
Therefore a new configuration system was built using configuration objects that are passed to the components of the framework. The XML files are now read directly using Apache Digester removing the need to build the configuration files repeatedly.
How it should be done in the future
Although this configuration system has worked well for a quite a while, now it is time to replace it with a new, more flexible approach for different reasons:
- Apache Digester is SAX based and therefore its capabilities are limited when it is used to parse a complex configuration syntax using nested elements.
- The way new configuration elements are added to an existing digester infrastructure is inflexible and error-prone due to the lack of XPath support.
- The namespace support of Digester is very limited, thus it is virtually impossible to create a modular configuration syntax.
All these reasons are major issues in our effort to make Pustefix more modular and IoC-capable. Therefore a new configuration system has to be build that at least complies with the following requirements:
- Configuration modules should be plug-ins which can be added to an existing infrastructure without the need of recompilation of existing code.
- The configuration system should provide full XPath and namespace support.
- Any configuration module should have access to the full document tree (using DOM or JDOM).
- Different configuration modules should be able to cooperate and depend on each other.
- Configuration modules should be able to delegate the processing of parts of their configuration to the framework, thus new configuration modules could even extend sub sections of parts parsed by other modules.
- Configuration objects created and configured by the various modules should use an abstraction level to provide a common base for cooperation between modules and further configuration processing.
In addition to that requirements there are "nice to haves" which are not absolutely needed but should be implemented if possible without much extra work:
- Configuration modules should provide a descriptor which contains all information needed to decide, when a specific configuration module is needed. By this means the overhead needed to load unused classes can be minimized.
Here is a first idea for the interfaces of the configuration framework:
The configuration is interpreted by different modules, which have an module descriptor (probably read from an XML file). This descriptor tells the configuration framework which parts of a configuration are read by a certain module.
The reading process has several (partly recursive) steps:
- The whole document is read. All XPath expressions provided by the module descriptors are evaluated and the result is cached. All rules provided by the deployment descriptors are checked for cyclic dependencies. If a cyclic dependency is detected, an error is reported.
- The reader creates a root ConfigurationNode object, looks for a rule matching the root element and calls the corresponding module.
- The module reads information from the DOM tree and creates corresponding configuration objects that are registered with the ConfigurationNode object. The module delegates the reading of sub-elements, it cannot process itself back to the reader using the ConfigurationReaderContext object.
- The reader looks for rules matching the nodes and calls the corresponding modules. If the node is an element, a new ConfigurationNode is created and set as the context node before calling the module(s).
- Proceed with step 3.
Configuration Process
The complete configuration process has three steps:
- Read the configuration file, create configuration objects (see above).
- Validate the configuration object structure (optional)
- Transform the configuration object structure to the application object structure.
The first step is rather easy and has been depicted above. The second step is even easier, as it does not have to be implemented by a module.
The third step however, can be rather complex: In a complex system configuration objects might contain information about how to handle other configuration objects. In other words the semantic of the configuration might be determined by the configuration itself.
However, the configuration of a specific object does not have to be that complicated. In most cases the structure is well-known and the transition from configuration to application objects can be easily performed.
Thus, there is a good migration path from the old configuration system to the new one: The current rules / configuration object scheme can be easily mapped on step one of the new concept. Step two is optional, so it can be omitted. Step three can be implemented by creating the application objects in a hard-wired way, using the configuration objects from step one.
This configuration model is flexible, so it can be used as a base for the new IoC-support. On the other hand it provides a good compatibility layer for legacy configuration formats and code.
Attachments
- configuration_interfaces.png (95.4 kB) - added by SebastianMarsching on 12/03/07 14:55:12.

