Props is super properties; containing all what is missing in JDK: UTF8 support, macros, sections, profiles, fully configurable... and more! Properties are stored in one or more *.props files, but its architecture is open for any type of source. Moreover, Props is compatible with Java properties.
Bellow is set of basic rules for props file format. Some of them are shown in the following example:
By default, props files are UTF8 encoded, but can be encoded in any encoding. Whatever encoding is used, Props will still load Java properties using ISO 8859-1.
Leading and trailing spaces will be trimmed from section names and property names. Leading and/or trailing spaces may be trimmed from property values.
Either equal sign (=) or colon (:) are used to assign property values.
Comments begin with either a semicolon (;), or a sharp sign (#) and extend to the end of line. It doesn't have to be the first character.
A backslash (\) escapes the next character (e.g., \# is a literal #, \\ is a literal \).
If the last character of a line is backslash (\), the value is continued on the next line with new line character included.
\\uXXXX is encoded as character. Also \t, \r and \f are encoded as characters.
Using Props is very easy. In a nutshell, properties are managed by Props class.
Props p = new Props();
p.load(new File("example.props"));
...
String story = p.getValue("story");
Properties can be loaded by Props in many different ways: from a File, InputStream, String or Properties. Then props are ready for usage and values can be looked up using getValue() method. This method always returns a String value.
Sections looks very much like Windows INI file sections. In Props, section simply represents the keys prefix for following keys, until the section end or end of file.
Section names are enclosed between [ and ]. Properties following a section header belong to that section. Section name is added as a prefix to section properties. Section ends with empty section definition [] or with new section start or end of file.
The following example:
[users.data] weight = 49.5 height = 87.7 age = 63 [] comment=this is base property
is identical to:
users.data.weight = 49.5 users.data.height = 87.7 users.data.age = 63 comment=this is base property
Sections, therefore, can shorten the file and make it more readable.
Often an application works in different environments and, therefore, require different set of (some) properties; for example: the development mode and deployment mode of a web application. One way how to organize properties is to define different profiles where the same key name takes different values.
Props supports property profiles. Profiles are defined within key name: profile names are enclosed between < and >. One key may contain one or more profile definitions. Also, profile definition can be anywhere in the key name, even in the middle of the word; however, it is a good practice to put them at the end.
Properties without a profile are base properties. If look up for a property of some profile fails, Props will examine the base properties. So profiles can be considered as a 'different views' or 'snapshots' of the same property set.
Example:
db.port=3086 db.url<develop>=localhost db.username<develop>=root db.url<deploy>=192.168.1.101 db.username<deploy>=app2499
In this example 3 keys are defined; two keys have different values in two profiles (develop and deploy) and no base value.
Since sections are just a prefix definition and since profile can be anywhere in the key name, therefore section name can contain profile definition as well. Above example can be written as:
db.port=3086 [db<develop>] url=localhost username=root [db<deploy>] url=192.168.1.101 username=app2499
When looking up for a value, it is possible to specify which profiles are active:
String url = props.getValue("db.url", "develop");
String user = props.getValue("db.username", "develop");
It is also possible to lookup only for base properties - using getBaseValue() method. Base properties are those that don't belong to any profile.
Usually, only one set of profiles is active for the application lifetime. Instead of passing active profiles to getValues() methods each and every time, Props allows to define so called default profiles externally, in the same props files used for loading properties.
Default profiles are active profiles when looking for a property using method getValue(String).
Default profiles can be set in the props files - this way the configuration set can be changed (i.e. active profiles can be modified) without the need to recompile the code. Default profiles are defined under key named @profiles. Example:
key1=hello key1<one>=Hi! @profiles=one
and the following Java code:
String value = props.getValue("key1");
would return the value 'Hi!', since default active profile is 'one'.
There are situations where two ore more profiles share the most of the configuration and only few properties are different (or: specific) for one profile (i.e. configuration). To avoid repeating of all properties for each profile, it is possible to define properties assigned to inner profiles only for those differences. Props will first lookup keys in inner profiles, then go up to the base level. Example:
key1<one>=Hi! key2<one>=... .... key100<one>=... key1<one.two>=Hola!
This example defines two profiles. First one is named 'one' and contains 100 properties. Second profile is an inner property named 'one.two'. It contains only 1 property (key1) - but all properties from its upper profile are available! So what happens when Java code calls the following: props.getValue("key1", "one.two")? Props will:
one.twoProps will check upper profile: oneThere can be many levels of inner profiles.
The biggest Props strength are macros. Macro is a reference to some keys value, used in value of other key. Macros are enclosed between ${ and }. Here is a simple example:
key1=Something ${foo}
...
foo=nice
Value of key1 is 'Something nice'. Macros can refer to any existing property key, no matter where it is defined.
Nested macros are also supported. Example:
key1=**${key${key3}}**
key3=2
key2=foo
Value of key1 is '**foo**'.
Props behavior can be fine-tuned using several configuration settings:
escapeNewLineValue - specifies the new line string when EOL is escaped. Default value is an empty string.valueTrimLeft - specifies should the values be trimmed from the left.valueTrimRight - specifies should the values be trimmed from the right.ignorePrefixWhitespacesOnNewLine - defines if the prefix whitespaces should be ignored when value is split into the lines.skipEmptyProps - skip empty properties.appendDuplicateProps - when set, duplicate props will not override existing one, but will be appended and separated by comma.There is IntelliJ IDEA plugin that provides support for Props files. For now, this support is very basic, but it will be enhanced soon.