blob: 401a75539590163d8a6b04362d1d11a489f6a85c [file] [log] [blame]
:jbake-type: page
:jbake-status: published
= Apache Tamaya -- Extension: Formats
toc::[]
[[Core]]
== Tamaya Formats (Extension Module)
=== Overview
Tamaya Formats is an extension module. Refer to the link:modules.html[extensions documentation] for further details.
Tamaya Formats provides an abstraction for configuration formats provding the following benefits:
* Parsing of resources in can be implemented separately from interpreting the different aspects/parts parsed. As an
example a file format can define different sections. Depending on the company specific semantics of the sections
a different set of +PropertySource+ instances must be created.
* Similarly the configuration abstraction can also be used as an interface for integrating Tamaya with alternate
frameworks that provide logic for reading configuration files, such as Apache commons.configuration.
=== Compatibility
The module is based on Java 7, so it can be used with Java 7 and beyond.
=== Installation
To benefit from dynamic value resolution you only must add the corresponding dependency to your module:
[source, xml]
-----------------------------------------------
<dependency>
<groupId>org.apache.tamaya.ext</groupId>
<artifactId>tamaya-formats</artifactId>
<version>{tamaya_version}</version>
</dependency>
-----------------------------------------------
=== The Idea
Formats should be reusable, meaning you should have to write a format parser only once and then be able to map the data read into whatever
data structure (in our cases: property sources).
==== ConfigurationData
Configuration formats can be very different. Some are simple key/value pairs, whereas other also consist of multiple sections (e.g. ini-files) or
hierarchical data (e.g. yaml, xml). This is solved in Tamaya by mapping the configuration read into a normalized intermediary format called
+ConfigurationData+:
[source,java]
.ConfigurationData
-------------------------------------------------------
public final class ConfigurationData {
public ConfigurationFormat getFormat();
public String getResource();
public Set<String> getSectionNames();
public Map<String,String> getSection(String name);
public boolean hasDefaultProperties();
public Map<String,String> getDefaultProperties();
public Map<String,String> getCombinedProperties();
public boolean isEmpty();
}
-------------------------------------------------------
In detail the data read from a file is organized into _sections_ as follows:
* with +getResource()+ and +getFormat()+ the underlying resource and the format that read this data can be accessed.
* properties can be owned by
** named sections
** an (unnamed) default section
* each section section contains a map of properties. Hereby the same key can be part of the default section and multiple
named sections, depending on the configuration format.
* The method +getSectionNames()+ returns a set of all section names.
* With +getSection(String name)+ a named section can be accessed.
* With +getDefaultSection()+ the 'default' section can be accessed. This is a convenience method.
* With +getCombinedProperties()+ a flattened entry map can be accessed built up (by default) out of
** all entries from the default section, without any changes.
** all entries from named sections, where the key for each entry is prefix with the section name and a '::' separator.
* The configuration format used determines the mapping of configuration data read into this structure. The format
implementation can as well provide alternate implementations of how the data read should be mapped into the
combined properties map.
==== ConfigurationFormat
A ConfigurationFormat is basically an abstraction that reads a configuration resource (modelled by an InputStream) and
creates a corresponding +ConfigurationData+ instance.
[source,java]
-------------------------------------------------------
public interface ConfigurationFormat {
String getName();
boolean accepts(URL url);
ConfigurationData readConfiguration(String resource, InputStream inputStream);
}
-------------------------------------------------------
=== How to tranform ConfigurationData into a PropertySource
For for the conversion of +ConfigurationData+ into a +PropertySource+ different approaches can be useful:
. The +ConfigurationFormat+ that reads the data can provides all properties read either as sectioned properties
or/and as default properties. The most simple cases is, where all properties have been added as 'default'
properties. In this case the default properties can be used as the property sources properties without any change.
. If the format did also add section based properties, the combined properties returned can be used, hereby
replacing the '::' separator with a '.' separator.
. In all other cases a custom mapping is useful, which can be acomplished by using the +MappedConfigurationDataPropertySource+
and overriding the +Map<String,String> populateData(ConfigurationData data)+ method.
In most cases the usage of a +FlattenedDefaultPropertySource+, is a good choice to start. This class
provides a convenient default mapping and also allows to customized the mapping easily:
[source,java]
-------------------------------------------------------
ConfigurationData data = ...;
FlattenedDefaultPropertySource ps = new FlattenedDefaultPropertySource(data){
protected Map<String, String> populateData(ConfigurationData data) {
...
}
};
-------------------------------------------------------
Nevertheless, depending on the context, where a configuration source was read (classloader, time, source etc.) the
resulting properties can have different semnatics, especially different priorities. Also section
names may be mapped into different ordinals instead of using them as key prefixes (e.g. imagine configuration formats
with a 'default', 'main', and 'overrides' sections). For such more complex or custom cases no simple mapping
can be defined. Consequently the functionality mapping the normalized +ConfigurationData+ read to the
appropriate collection of +PropertySource+ instances must be implemented.
=== Examples
==== Mapping ini-Files
Consider the following ini-file:
[source,listing]
.Example.ini
-------------------------------------------------------
a=valA
a.b=valB
[section1]
aa=sectionValA
aa.b.c=SectionValC
[section2]
a=val2Section2
-------------------------------------------------------
This file content coud be mapped to the following structure:
[source,listing]
.Mapping of Example.ini
-------------------------------------------------------
a=valA
a.b=valB
section1::valA=sectionValA
section1::a.b.c=SectionValC
section2::a=val2Section2
-------------------------------------------------------
Nevertheless from the +ConfigurationData+ instance a more complex algorithm can access all the different parts:
* the_default_ properties (a, a.b)
* the section +section1+, with properties +aa, aa.b.c+
* the section +section2+, qith properties +a+
==== Mapping xml-Files
The same concept can also be applied to xml-files. Consider the following configuration file:
[source,xml]
.Example.conf
-------------------------------------------------------
<config>
<default>
<a>valA</a>
<a.b>valB</a.B>
</default>
<section id="section1">
<param id="aa">sectionValA</aa>
<param id="aa.b.c">SectionValC</aa.b.c>
</section>
<section id="section2">
<param id="a">val2Section2</aa>
</section>
</config>
-------------------------------------------------------
This file basically describes the same configuration as the ini-based version we have seen before. The formats
module hereby ships with 3 format classes:
* +PropertiesFormat+ providing support for .properties files.
* +PropertiesXmlFormat+ providing support for xml.property files.
* +IniConfiguratonFormat+ providing support for xml.property files.