XConf

XML Configuration File Processor

It is currently common to build a number of releases from a single code base. For example, a development release,
a QA release, a production release and perhaps customer-specific releases. However, these releases seem to differ
mostly in the contents of their XML configuration files, and then only very little. Maintaining all these slightly
different configuration files is a real nuisance.

XConf was created to simplify this maintenance. Its fundamental premise is that a single development-release (or
production-release) configuration file is created and maintained, and is processed by XConf at either build or deployment
time into an appropriate release by applying one or more XML-based scripts. Each script contains only the differences
required to create the appropriate release, thus removing the need for the mass duplication of configuration files.

This is not really a new solution, since XSLT has been used in the past to do this quite successfully, but XPath
can get a little arcane, and maintaining transformation scripts using XSLT can become really complex very quickly.
XConf uses a very simple and compact method of specifying elements that need to be processed, and provides some
very useful constructs to make transformations painless.

By default, XConf does not validate the input XML file, and if a Xerces SAX parser is present
in the classpath, XConf completely ignores any DTDs. All it really requires is a well-formed
XML file. You can force XConf to validate any input XML files it processes by adding the optional
-validate argument to the invocation command line.

XML File Processing

By default, XConf assumes that both the configuration and input files are XML. You can direct it to process
them as properties files by adding the optional -properties to the invocation command line. In that
case, the configuration file should be a properties file in the format described by properties
file processing, and the input file should also be a valid properties file.

Each xconf task can contain token elements that represent replacement tokens for processing directives.
These are usually supplied to the command line instance from a properties file referenced by the -tokens
optional flag. These token elements will replace any conflicting values provided via the tokens attribute.

NOTE: All processing actions require a path attribute to define the XML
element(s) in the input file that will be altered/processed. See the
Path Syntax section below for a description
of the contents of this attribute.

1. setText

- sets the text content of an XML element
- uses a <text> child element to define the new text content
- the text element's content can contain replacement tokens.
- Example: Set the session timeout of a web application to 20 minutes:

- As of release 1.9.6 the text child element is optional, so you could rewrite the above as:

<setText path="web-app/session-config/session-timeout">20</setText>

2. removeText

- removes the text content of an XML element
- Example: Remove the title from an XHTML document:

<removeText path="html/head/title"/>

3. setAttribute

- sets and adds attributes to an XML element
- requires <attribute> child elements to provide the name and value for each new attribute
- attribute name and value can contain replacement tokens.
- Example: Set the path of all tiles definitions:

All these nice processing actions would be pretty pointless without a compact way of describing the elements
that need to be processed. If this syntax looks similar to XPath, it is not an accident.

Each element in an XML document tree is defined by a node in the path string.

path := node/node/node
node := element OR element[constraint] OR .. (parent element)
element := name of XML element OR * (all elements)
constraint := [+-]cType|cName(|cValue)
[+-] := + (include), - (exclude)
cType := a (attribute), t (text), c (child)
cName := name of attribute for (a), normalized text for (t), name of child element for (c)
cValue := optional, for (a) it is the value of named attribute, and for (c) it is the
normalized text content of the child element

So, for example if we want to remove the singleton attribute only from spring
beans that have an attribute named "id" with a value of "dataSource", we would
create a removeAttribute action with:

- can be used to define a single path node for reuse in multiple path definitions.
- can also be used to define nodes that contain '/' characters, which will otherwise lead to
path attribute parsing errors. For example:

element[+a|url|http://localhost.com]

- it is referenced in action path attributes by enclosing its name in '${' and '}' characters.
- Example: redefine a Spring DataSource's jndi name:

- can be used to define a complete or partial path for reuse in multiple action path attributes.
- may be composed of previously declared path and node constant references.
- it is referenced in action path attributes by enclosing its name in '${' and '}' characters.
- Example: perform multiple processing actions on a web-app filter definition:

Processing of properties files is currently done in a very simple manner. At the start of
processing, 2 properties files are read by XConf. The first is a config file describing
the processing actions to take, and the second is an input file to be processed. The config
file is then used to process the input file, creating an output properties file that contains
the results of the processing.

Any property in the config file with a value of "XCONFDELETE",
if present in the input file, will not appear in the output file.

All other properties in the config file will simply be set as properties in
the output file using values from the config file, possibly overwriting properties
that exist in the input file. The properties from the config file can contain replacement tokens
(ie. ${key}) to be replaced with values from either the configproperties
file when run as a standalone application, or from properties in the ant project when
run as an ant task.

All input file properties that are not overwritten or deleted by the config file,
are simply copied into the output file.

Please note that the order of properties from the input file is not preserved in
the output file, since the java.util.Properties API does not guarantee the
order of key/value pairs.