Props
Super properties
Jodd Props is Java properties
on steroids: containing all that is missing in JDK: UTF-8 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.
Jodd Props is compatible with Java properties.
The purpose of Jodd Props is not to provide the ultimate configuration solution, but to replace Java properties with a similar but better alternative. If you are already using Java properties in your application, or just want a familiar and friendly configuration, consider switching to Jodd Props.
Features
Jodd Props configuration is stored in text file(s), with default extension .props
. Still, Props is compatible with Java properties and knows to load *.properties
as well. Here is screenshot of a props
file:
UTF-8 encoding
By default, props
files are UTF-8
encoded. However, Jodd Props will always load Java's properties using ISO 8859-1
.
Trimming whitespaces
Leading and trailing spaces will be trimmed from section names and property names. Leading and/or trailing spaces may be optionally trimmed from property values.
Assignment of values
Either equal sign (=
) or colon (:
) are used to assign property values.
Quick appended values
Use +=
to append values (separated by comma) of properties with the same name.
Comments
Comments begin with either a semicolon (;
), or a sharp sign (#
) and extend to the end of a line. It doesn't have to be the first character of a line.
Escaping
The backslash (\
) escapes the next character (e.g., \#
is a literal #
, \\
is a literal \
).
Multi-line values
If the last character of a line is backslash (\
), the value is continued on the next line with a new line character included.
Special characters
\\uXXXX
is encoded as a character. Also \t
, \r
and \f
are encoded as characters as well.
Triple quotes
Use triple-quote to define multi-line values in a convenient way.
Basic usage
Using Jodd Props is very easy. In a nutshell, properties are managed by Props
class.
Properties can be loaded by Props
in many different ways: from a File
, InputStream
, String
or Properties
. Once loaded, props are ready for usage. Values can be looked up using the getValue()
method. This method always returns a String
value.
Sections
In Props, a section simply defines a key's prefix for the following set of keys, until the section or file ends.
Section name is enclosed between [
and ]
. Properties following a section definition belong to that section. Section name is added as a prefix to section properties. Section ends with empty section definition []
or with a new section start or end of the file.
The following example:
is identical to:
Sections can shorten the file and make it more readable.
Profiles
Often an application works in different environments and, therefore, requires a different set of properties. For example, the development mode and deployment mode of an 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 the key name: profile name is enclosed between <
and >
. One key may contain one or more profile definitions. Profile definition can exist 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 lookup for a property of some profile fails, Props will then examine the base properties.
Profiles can be considered as 'different views' or 'snapshots' of the same property set.
Example:
In this example 3 keys are defined; two keys have different values in two profiles (develop
and deploy
) and have no base value.
Since sections are just a prefix definition and since profile can be located anywhere in the key name, the section name can define profile definition as well! The above example can be written as:
When looking up for a value, it is possible to specify which profiles are active:
More than one profile can be specified at a time. The order of specified profiles is important! When one key is defined in more than one active profile, the FIRST value (of first matched profile) is returned. {: .attn}
It is also possible to lookup only for base properties (ignoring the profiles) - using getBaseValue()
method. Base properties are those that don't belong to any profile.
Default active profiles
Usually, only one set of profiles is active during the application's lifetime. Instead of passing active profiles to getValues()
methods each and every time, Props allows defining so-called active profiles externally, in the same props
files used for loading properties.
The active profiles are the default when looking for a property using method getValue(String)
.
Active profiles can be defined 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. Active profiles are defined under the special base key named @profiles
. Example:
and the following Java code:
would return the value Hi!
, since the active profile is one
.
Active profiles can be set from Java, too, using the method: setActiveProfiles()
.
Inner profiles
There are situations where two or more profiles share the most of the configuration and only a 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:
This example defines two profiles. The first one is named one
and contains 100 properties. The second profile is an inner property named one.two
. It contains only 1 property (key1
) - but all properties from its upper profile are available! What happens when Java code calls the following: props.getValue("key1", "one.two")
? Props will:
lookup for property in inner profile
one.two
if a value is not found, Props will check upper profile:
one
if a value is not found and there are no more upper profiles, Props will check base properties.
There can be any levels of inner profiles.
Macros
The biggest Props strength are macros. Macro is a reference to a value of some other key. Macros are enclosed between ${
and }
. Here is a simple example:
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:
Value of key1
is !!foo!!
.
Macros and profiles
Macros are always resolved using the currently active or provided profile. The value of macro may change if current profile is changed.
This behavior is controlled with flag: useActiveProfilesWhenResolvingMacros
. Here is an example:
What is the value of data.path
when foo
profile is set as active? Since foo
is active, root
value becomes /foo
, therefore the data.path
is going to be set to /foo/data
value.
If we turn off profiles (and use only base propertes), the data.path
value is going to be /app/data
.
It is also possible to explicitly set macro's profile:
In this example, root
macro will always use the foo
profile regardless of the currently selected profiles. So data.path
value would be always be: /foo/data
.
Multiline values
When enabled, multilines values may be defined with triple-quotes. Everything between is considered as a value. Example:
Note that multiline values are NOT trimmed! Therefore, the value from the example will consist of 5 rows.
Iteration and keys order
Props keeps your keys in order! It is possible to iterate all Props keys in the same order as they are listed in the props file(s). So instead doing this:
you can simply iterate props using:
The order of iteration is the same as the order of props definitions!
But there is more - it is possible to furthermore tune the iterator, by adding a filter for profiles to look and/or sections to iterate. So you can write something like this:
to iterate only props in given section and for given profiles.
Due to profiles usage, one key may be defined on many places in props file. For example, you can specify value for two profiles. In this case, it is not clear what is the 'correct' order of the keys: should it be the first appearance of the key, or the place where it gets its value? Either way, you can control this behavior using skipDuplicatesByValue()
and skipDuplicatesByPosition()
during iterator building.
Copy operator
Imagine you have certain number of properties that is, by default, the same for some number of different categories. For example:
Props allows you to use copy operator: <=
to minimize duplication of the props. Above props can be written as:
This example shows three different ways how to use copy operator, without a section, with partial section name or with full section name. All three ways are identical and it's up to you which one you gonna use.
Remember that copied values are set as macros, so all above copied properties are identical to:
All rules for resolving macros applies.
Last updated