Mixed XML and Property files?

Have you ever wanted the simplicity of a plain old Java properties file but with just a little bit of grouping provided by XML? I’ve been working on a small side-project recently and it requires a simple configuration file of a dozen items or so. The project needed a repeatable set of configuration parameters, so that it could connect to several SVN servers. Each connection needed a URL, username, password, and a few other ancillary properties. This is a pain to do in a plain old properties file. You have to do something with the naming of the properties to relate them together, such as:

property.1.url = http://...
property.1.username = Bob
property.1.password = Bob’s secret

property.2.url = http://...
property.2.username = Joe
property.2.password = Joe’s secret  

This way works but it’s sort of annoying and can be confusing for someone else to understand what’s going on. They would likely need to read the documentation, especially if it’s more complex with multiple types of repeating parameters. There are several alternatives, you could try encoding all the parameters into one property but that’s even harder for a user to figure out. A slightly better alternative is to use something hierarchical like XML, thus:

<properties>
	<repeatable>
		<url>http://...</url>
		<username>Bob</username>
		<password>Bob’s secret</password>
	</repeatable>
	<repeatable>
		<url>http://...</url>
		<username>Joe</username>
		<password>Joe’s secret</password>
	</repeatable>
</properties>

This is easier to understand, but it’s very verbose. Each property is labeled twice, once to open the tag and again to close the tag. XML is good for complex things like HTML or specific file formats with a dedicated reader. However, XML is not great for humans to read, let alone edit quickly.

A better solution, combine both!

Instead of either XML or properties file we can munge the two together to create something that is easier for users to manage.

property.one = value1
property.two = value2

<repeatable>
	url = http://...
	username = Bob
	password = Bob’s secret
</repeatable>

<repeatable>
	url = http://...
	username = Joe
	password = Joe’s secret
</repeatable>

The combined format is similar to Apache’s httpd configuration format where name/value pairs are also mixed with nestable elements. It’s very close to the simplicity of a plain old properties file, but has just enough expressivity to handle grouping of elements. It’s a win-win.

How to parse the mixed format?

It’s easy, thanks to the Apache’s Common Configuration project! Here’s a simple class to demonstrate parsing the combined file format. The full class can be downloaded here. Below I will walk through the important bits that make processing the config file easy:

private final static String PREPEND = "<?xml version=\"1.0\"?>\n<xml>\n";
private final static String APPEND = "\n</xml>";

List<InputStream> inputStreams = new ArrayList<InputStream>(); inputStreams.add(new ByteArrayInputStream(PREPEND.getBytes())); inputStreams.add(new FileInputStream(configFile)); inputStreams.add(new ByteArrayInputStream(APPEND.getBytes())); InputStream combinedInputStreams = new SequenceInputStream( Collections.enumeration(inputStreams));

This is a little trick to makes the configuration file a well formed XML document. It pre-pends an XML declaration and root level element to the beginning of the document while closing it at the end. All these parts are combined together using a SequenceInputStream which just reads from the first inputstream before moving on to the next on the list until all the inputstreams have been exhausted. Next we move on to parsing the configuration file.

XMLConfiguration xmlConfiguration = new XMLConfiguration();
xmlConfiguration.setDelimiterParsingDisabled(true);
xmlConfiguration.load(combinedInputStreams);

Next, we simply read the configuration file as a normal XML-based configuration file using the standard Apache commons library. One thing to note is that before reading in the configuration file it is a good thing to disable DelimiterParsing which is enabled by default. Normally the configuration parser will separate multiple values separated by a comma into a list, but since at this level each value is really a sub-properties file it gets in the way if a comma appears anywhere in the document. Next we move on to reading the root-level properties.

String rootConfig = xmlConfiguration.getString("");
PropertiesConfiguration rootProperties = new PropertiesConfiguration();
rootProperties.load(new ByteArrayInputStream(rootConfig.getBytes()));

Here we take all the text that is at the root of the configuration file (aka. Everything that is not inside a tag) and parse that as a properties file. Similar to how we read in the XML portion, this time we just pass all the text into the Apache’s PropertiesConfiguration object. From that object you can get all the properties parsed for strings, numbers, lists, etc by there names. After this point it’s just as simple as processing a properties file. Lastly, we move on to show how to process the repeatable sub-sections.

List<String> repeatableConfigs = xmlConfiguration.getList("repeatable");
for (String repeatableConfig : repeatableConfigs) {
	PropertiesConfiguration repeatableProperties = 
		new PropertiesConfiguration();
	repeatableProperties.load(
		new ByteArrayInputStream(repeatableConfig.getBytes()));

	// Do something...
}

Here we get as a single list all the nestable elements with the name “repeatable”. From there we do the same thing as before, hand them to the PropertiesConfiguration object to parse the properties. It’s all pretty simple.

Thoughts

This is an elegant solution that forges a compromise between the expressiveness of XML and the simplicity of a plain old Java property file. This method is easy to implement and feels natural to any system administrator who’s use to editing configuration files on a regular basis.

There is one gotcha though, since we process the document as if it were an XML document all XML entities must be escaped. So if you need to include &, <, or > then you’ll need to escape them with “&amp;”, “&lt;”, or “&gt;” or else you’ll create parsing exceptions.

Download the full class: ConfigurationExample.java

Tags: , ,

Leave a Reply