Cleanup: Configuration: 1.0
- Proposal for Agavi 1.0
- Authors:
The XML configuration system pre-Agavi 1.0 is technically capable of supporting multiple formats (INI, XML, and so on). It turns out that nobody cares about anything besides XML, so support for other handlers is being removed in favor of exclusive XML support.
Basic concepts
In pre-Agavi 1.0 releases, all configuration was contained within the http://agavi.org/agavi/1.0/config namespace. For 1.0, these namespaces are separated:
- The envelope namespace http://agavi.org/agavi/config/global/envelope/1.0 contains the configurations, configuration, parameters, and parameter elements
- Each item in the configuration uses its own namespace, which must be registered inside http://agavi.org/agavi/config/parts
- Example: factories.xml uses the namespace http://agavi.org/agavi/config/parts/factories/1.0
- An internal annotation namespace http://agavi.org/agavi/config/global/annotations/1.0 is used to manage elements within the parser and handlers (for example, to determine which elements have matched a given criterion)
Backwards compatibility
Since this would invalidate Agavi 0.11 configurations, XSL transformations are used to convert existing configurations from previous versions to the latest version.
Merge rules
Since configuration files may reference other files as their parents, methods for merging parameters with identical names must be defined. For pre-1.0 configuration handlers, merging was handled by config handlers and was not configurable. This is not ideal.
Merge behaviors could be implemented using the attribute merge in the http://agavi.org/agavi/config/global/envelope/1.0 namespace. Currently, there are ideas for two merge behaviors for elements:
- merge="combine": Recursively combines all elements of the given parent element, replacing old elements with new ones
- merge="replace": Replaces the contents of the old parent element with the contents of the new parent element
The default merge behavior would be backwards-compatible with Agavi 0.11.
An example:
<?xml version="1.0" encoding="UTF-8"?> <ae:configurations xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0" xmlns="http://agavi.org/agavi/config/parts/factories/1.0"> <!-- ... --> <ae:configuration context="soap"> <!-- ... --> <routing class="AgaviSoapRouting"> <ae:parameter name="wsdl_generator"> <ae:parameter name="soap"> <ae:parameter name="address"> <ae:parameter name="location">http://localhost/~dzuelke/_projects/agavi/branches/0.11/samples/pub/soap.php</ae:parameter> </ae:parameter> <ae:parameter name="body"> <ae:parameter name="use">literal</ae:parameter> </ae:parameter> <ae:parameter name="header"> <ae:parameter name="use">encoded</ae:parameter> </ae:parameter> </ae:parameter> <ae:parameter name="global_headers"> <ae:parameter name="request"> <ae:parameter> <ae:parameter name="message">yay:soapHeaders</ae:parameter> <ae:parameter name="part">foo</ae:parameter> </ae:parameter> <ae:parameter> <ae:parameter name="message">yay:soapHeaders</ae:parameter> <ae:parameter name="part">bar</ae:parameter> </ae:parameter> </ae:parameter> </ae:parameter> </ae:parameter> </routing> </ae:configuration> </ae:configurations>
And now we need to override the location for the "production" environment. Of course we could simply define it in a configuration directive, but let's ignore that workaround for a moment. The only way to do it at the moment is to replicate the entire <parameter name="wsdl_generator"> block in the new definition. It would be much nicer to be able to do this:
<?xml version="1.0" encoding="UTF-8"?> <ae:configurations xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0" xmlns="http://agavi.org/agavi/config/parts/factories/1.0"> <!-- ... --> <ae:configuration context="soap"> <!-- ... --> <routing class="AgaviSoapRouting"> <ae:parameter name="wsdl_generator"> <ae:parameter name="soap"> <ae:parameter name="address"> <ae:parameter name="location">http://localhost/~dzuelke/_projects/agavi/branches/0.11/samples/pub/soap.php</ae:parameter> </ae:parameter> <ae:parameter name="body"> <ae:parameter name="use">literal</ae:parameter> </ae:parameter> <ae:parameter name="header"> <ae:parameter name="use">encoded</ae:parameter> </ae:parameter> </ae:parameter> <ae:parameter name="global_headers"> <ae:parameter name="request"> <ae:parameter> <ae:parameter name="message">yay:soapHeaders</ae:parameter> <ae:parameter name="part">foo</ae:parameter> </ae:parameter> <ae:parameter> <ae:parameter name="message">yay:soapHeaders</ae:parameter> <ae:parameter name="part">bar</ae:parameter> </ae:parameter> </ae:parameter> </ae:parameter> </ae:parameter> </routing> </ae:configuration> <ae:configuration environment="production.*" context="soap"> <routing class="AgaviSoapRouting"> <ae:parameter name="wsdl_generator"> <ae:parameter name="soap"> <ae:parameter name="address"> <ae:parameter merge="replace" name="location">http://localhost/~dzuelke/_projects/agavi/branches/0.11/samples/pub/soap.php</ae:parameter> </ae:parameter> </ae:parameter> </ae:parameter> </routing> </ae:configuration> </ae:configurations>
The exact behavior has yet to be defined. We need to think about what happens in the case above... are siblings of nodes on the way down to the location parameter simply ignored (there are none in this example)? Or are they treated like combine mode parameters? Do we allow combine inside replace, or only the other way round? And so on. By default, the mode would be replace, defined on each of the first level of parameters.
Or maybe we need a merge mode, too? That's like combine, but not recursive? And by default applies to the outermost <parameters> element?
Process flow
For a given configuration file, processing follows this procedure:
- Parse the configuration file
- prepare()
- Validate XMLSchema-instance on the file
- Perform pre-transformation individual file validation
- transform()
- Apply rules for individual files from <transform> in config_handlers.xml
- Apply <?xml-stylesheet?> processing directives
- Perform post-transformation individual file validation
- cleanup()
- If the configuration file has a parent= attribute, repeat steps 1 through 6 on the document referenced by that attribute
- Combine the configuration files with all of its parents according to merge rules
- Order and filter the combined configuration file
- Perform pre-transformation combined file validation
- transform()
- Apply rules for combined files from <transform> in config_handlers.xml
- Perform post-transformation combined file validation
Example
This is a sample factories.xml configuration file:
<ae:configurations xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0" xmlns="http://agavi.org/agavi/config/parts/factories/1.0"> <ae:configuration> <controller class="AgaviController" /> <database_manager class="AgaviDatabaseManager" /> <dispatch_filter class="AgaviDispatchFilter" /> <execution_container class="AgaviExecutionContainer" /> <execution_filter class="AgaviExecutionFilter" /> <filter_chain class="AgaviFilterChain" /> <logger_manager class="AgaviLoggerManager" /> <request class="AgaviWebRequest" /> <response class="AgaviWebResponse" /> <routing class="AgaviWebRouting" /> <security_filter class="AgaviSecurityFilter" /> <storage class="AgaviSessionStorage" /> <user class="AgaviSampleAppUser" /> <translation_manager class="AgaviTranslationManager" /> <validation_manager class="AgaviValidationManager"> <ae:parameter name="mode">strict</ae:parameter> </validation_manager> </ae:configuration> </ae:configurations>

