Property value substitution
In an environment with multiple IG instances, you can require similar but not identical configurations across the different instances.
Property value substitution enables you to do the following:
-
Define a configuration that is specific to a single instance, for example, setting the location of the keystore on a particular host.
-
Define a configuration whose parameters vary between different environments, for example, the URLs and passwords for test, development, and production environments.
-
Disable certain capabilities on specific nodes.
Property value substitution uses configuration tokens to introduce variables into the server configuration. For information, see Configuration Tokens.
The substitution follows a process of token resolution, JSON evaluation, and data transformation, as described in the following sections:
Configuration Tokens
A configuration token is a simple reference to a value. When configuration tokens are resolved, the result is always a string. Transformation described in Transformations can be used to coerce the output type.
Configuration Tokens for File System
IG provides ig.instance.dir
and
ig.instance.url
to define the file system directory and
URL for configuration files.
Their values are computed at startup, and evaluate to a directory such as
$HOME/.openig
(%appdata%\OpenIG
). You can use these
tokens in your configuration without explicitly setting their values.
For information about how to change the default values, see Configure default location.
Syntax
Configuration tokens follow the syntax &{token[|default]}
, as follows:
-
Are preceded by an ampersand,
&
-
Are enclosed in braces,
{}
-
Define default values with a vertical bar (
|
) after the configuration token -
Are in lowercase
-
Use the period as a separator,
.
When a configuration token is supplied in a configuration parameter, it is always inside a string enclosed in quotation marks, as shown in the following example:
"&{listen.port|8080}"
To escape a string with the syntax of a configuration token, use a backslash
(\
). The following string is treated as normal text:
"\&{listen.port|8080}"
A configuration property can include a mix of static values and expressions, as shown in the following example:
"&{hostname}.example.com"
Configuration tokens can be nested inside other configuration tokens as shown in the following example:
"&{&{protocol.scheme}.port}"
Default values or values in the property resolver chain can be nested, as shown in the following example:
"&{&{protocol.scheme|http}.port|8080}"
JSON Evaluation
JSON evaluation is the process of substituting configuration tokens and transforming JSON nodes for an entire JSON configuration. After JSON evaluation, all configuration tokens and transformations in the configuration are replaced by values.
At startup, IG evaluates the configuration tokens in
config.json
and admin.json
. When routes are deployed,
IG evaluates the configuration tokens in the route.
Configuration tokens are matched with tokens available in the chain of resolvers, and the configuration token is substituted with the value available in the resolver. For information about each of the resolvers mentioned in the following section, see Token Resolution.
IG searches for matching tokens in the chain of resolvers, using the following order of precedence:
-
Local resolver:
The route resolver for the route being deployed
-
Intermediate resolver:
All intermediate route resolvers (for example, for parent routes to the route being deployed) up to the bootstrap resolver
-
Bootstrap resolver:
-
Environment variables resolver
-
System properties resolver
-
Token source file resolvers
-
Hardcoded default values
-
The first resolver that matches the token returns the value of the token.
If the token can’t be resolved, IG uses the default value defined with the configuration token. If there is no default value, the token can’t be resolved and an error occurs:
-
If the configuration token is in
config.json
oradmin.json
, IG fails to start up. -
If the configuration token is in a route, the route fails to load.
When configuration tokens are nested inside other configuration tokens, the tokens are evaluated bottom-up, or leaf-first. For example, if the following configuration token takes only the default values, it is resolved as follows:
-
"&{&{protocol.scheme|http}.port|8080}"
-
"&{http.port|8080}"
When
&{protocol.scheme|http}
takes the default valuehttp
. -
"8080"
When
&{http.port|8080}
takes the default value8080
.
If the configuration includes a transformation, IG applies the transformation after the token is substituted. When transformations are nested inside other transformations, the transformations are applied bottom-up, or leaf-first. For more information, see Transformations.
Token Resolution
At startup, the bootstrap resolver builds a chain of resolvers to resolve
configuration tokens included in config.json
and admin.json
.
When a route is deployed, route resolvers build on the chain to add
resolvers for the route.
Route Token Resolvers
When a route is deployed in IG a route resolver is created to resolve
the configuration tokens for the route. The resolvers uses token values defined
in the properties
section of the route.
If the token can’t be resolved locally, the route resolver accesses token values recursively in a parent route.
For more information, about route properties, see Route properties.
Environment Variables Resolver
When the bootstrap resolver resolves a configuration token to an environment
variable, it replaces the lowercase and periods (.
) in the token to
match the convention for environment variables.
Environment variable keys are transformed as follows:
-
Periods (.) are converted to underscores
-
All characters are transformed to uppercase
The following example sets the value of an environment variable for the port number:
$ export LISTEN_PORT=8080
In the following IG configuration, the value of port
is 8080
:
{
"port": "&{listen.port}"
}
System Properties Resolver
The system property name must match a configuration token exactly. The following example sets a system property for a port number:
$ java -Dlisten.port=8080 -jar start.jar
In the following IG configuration, the value of port
is 8080
:
{
"port": "&{listen.port}"
}
Token Source File Resolvers
Token source files have the .json
or .properties
extension.
The bootstrap resolver uses the files to add file resolvers to the chain of
resolvers:
-
JSON file resolvers
Token source files with the
.json
extension take a JSON format. The token name is mapped either to the JSON attribute name or to the JSON path.Each of the following
.json
files set the value for the configuration tokenproduct.listen.port
:{ "product.listen.port": 8080 }
{ "product.listen": { "port": 8080 } }
{ "product": { "listen": { "port": 8080 } } }
-
Properties file resolvers
Token source files with the
.properties
extension are Java properties files. They contain a flat list of key/value pairs, and keys must match tokens exactly.The following
.properties
file also sets the value for the tokenslisten.port
andlisten.address
:listen.port=8080 listen.address=192.168.0.10
Token source files are stored in one or more directories defined by the
environment variable IG_ENVCONFIG_DIRS
or the system
property ig.envconfig.dirs
.
If token source files are in multiple directories, each directory must be specified in a comma-separated list. IG doesn’t scan subdirectories. The following example sets an environment variable to define two directories that hold token source files:
$ export IG_ENVCONFIG_DIRS="/myconfig/directory1,/myconfig/directory2"
At startup, the bootstrap resolver scans the directories in the specified order, and adds a resolver to the chain of resolvers for each token source file in the directories.
Although the bootstrap resolver scans the directories in the specified order, within a directory it scans the files in a nondeterministic order.
Note the following constraints for using the same configuration token more than once:
-
Do not define the same configuration token more than once in a single file. There is no error, but you won’t know which token is used.
-
Do not define the same configuration token in more than one file in a single directory. An error occurs.
This constraint implies that you can’t have backup .properties
and.json
files in a single directory if they define the same tokens. -
You can define the same configuration token once in several files that are located in different directories, but the first value that IG reads during JSON evaluation is used.
When logging is enabled at the DEBUG level for token resolvers, the origin of the token value is logged.
If you are using the default logback implementation, add the following line to your
logback.xml
to enable logging:<logger name="org.forgerock.config.resolvers" level="DEBUG" />
Transformations
A set of built-in transformations are available to coerce strings to other data types. The transformations can be applied to any string, including strings resulting from the resolution of configurations tokens.
After transformation, the JSON node representing the transformation is replaced by the result value.
The following sections describe how to use transformations, and describe the transformations available:
Usage
{
"$transformation": string or transformation
}
A transformation is a JSON object with a required main attribute, starting with
a $
. The following example transforms a string to an integer:
{"$int": string}
The value of a transformation value can be a JSON string or another transformation that results in a string. The following example shows a nested transformation:
{
"$array": {
"$base64:decode": string
}
}
The input string must match the format expected by the transformation. In the
previous example, because the final transformation is to an array, the input
string must be a string that represents an array, such as
"[ \"one\", \"two\" ]"
.
In the first transformation, the encoded string is transformed to a
base64-decoded string. In the second, the string is transformed into a JSON
array, for example, [ "one", "two" ]
.
array
{"$array": string}
Returns a JSON array of the argument.
Argument | Returns |
---|---|
|
|
The following example transformation results in the JSON array
[ "one", "two" ]
:
{"$array": "[ \"one\", \"two\" ]"}
bool
{"$bool": string}
Returns true
if the input value equals "true"
(ignoring case). Otherwise,
returns false
.
Argument | Returns |
---|---|
|
|
If the configuration token &{capture.entity}"
resolves to "true"
, the
following example transformation results in the value true
:
{"$bool": "&{capture.entity}"}
decodeBase64
{
"$base64:decode": string,
"$charset": "charset"
}
Transforms a base64-encoded string into a decoded string. If $charset
is
specified, the decoded value is interpreted with the character set.
Argument | Parameters | Returns |
---|---|---|
|
|
|
The following example transformation returns the Hello
string:
{
"$base64:decode": "SGVsbG8=",
"$charset": "UTF-8"
}
encodeBase64
{
"$base64:encode": string,
"$charset": "charset"
}
Transforms a string into a base64-encoded string. Transforms to null
if the
string is null
.
If $charset
is specified, the string is encoded with the character set.
Argument | Parameters | Returns |
---|---|---|
|
|
|
int
{"$int": string}
Transforms a string into an integer.
If the parameter is not a valid number in radix 10, returns null
.
Argument | Returns |
---|---|
|
|
The following example transformation results in the integer 1234
:
{"$int": "1234"}
list
{"$list": string}
Transforms a comma-separated list of strings into a JSON array of strings
Argument | Returns |
---|---|
|
|
The following example transformation results in the array of strings
["Apple","Banana","Orange","Strawberry"]
:
{"$list": "Apple,Banana,Orange,Strawberry"}
The following example transformation results in the array of strings
["Apple"," Banana"," Orange"," Strawberry"]
, including the untrimmed spaces:
{"$list": "Apple, Banana, Orange, Strawberry"}
The following example transformation results in the array of strings
["1","2","3","4"]
, and not an array of JSON numbers [1,2,3,4]
:
{"$list": "1,2,3,4"}
number
{"$number": string}
Transform a string into a Java number, as defined in Class Number.
Argument | Returns |
---|---|
|
|
The following example transformation results in the number 0.999
:
{"$number": ".999"}
object
{"$object": string}
Transforms a string representation of a JSON object into a JSON object.
Argument | Returns |
---|---|
|
|
The following example transformation
{"$object": "{\"ParamOne\":{\"InnerParamOne\":\"InnerParamOneValue\",\"InnerParamTwo\": false}}"}
results in the following JSON object:
{
"ParamOne": {
"InnerParamOne": "myValue",
"InnerParamTwo": false
}
}
string
{"$string": placeholder string}
Transforms a string representation of a JSON object into a placeholder string. Placeholder strings are not encrypted.
Use this transformation for placeholder strings that that must not be encrypted.
Argument | Returns |
---|---|
|
|
This example transformation:
{
"someAttributeExpectingString": { "$string": "&{ig.instance.dir}" }
}
results in this JSON object:
{
"someAttributeExpectingString": "/path/to/ig"
}