Expressions
Use expressions that conform to the Unified Expression Language in JSR-245 to specify configuration parameters as expressions in routes. The result of an expression must match the expected type. For examples of expressions used in routes, refer to Examples.
Use expressions in routes for the following tasks:
- Evaluate object properties
-
The following example returns the URI of the incoming request:
${request.uri}
- Call functions decribed in Functions
-
Functions can be operands for operations and can yield parameters for other function calls.
The following example uses the function
find
to test whether the request URI is on the/home
path:${find(request.uri.path, '^/home')}
- Call Java methods
-
The following example uses the method Java
String.startsWith()
to test whether the request starts with/home
:${request.uri.path.startsWith("/home")}
- Retrieve Java system properties
-
The following example yields the home directory of the user executing the IG process:
${system['user.home']}
- Retrieve environment variables
-
The following example yields the home directory of the user executing the IG process:
${env['HOME']}
The following example yields the path value of the
keystore.p12
file in the user’s home directory. The result is just a String concatenation, there is no verification that the file actually exists:${env['HOME']}/keystore.p12`
- Perform logical operations such as
and
,or
, andnot
-
The following expression uses the operators
and
andor
to determine where to dispatch a request. The expression is used in Share JWT sessions between multiple instances of IG:${find(request.uri.path, '/webapp/browsing') and (contains(request.uri.query, 'one') or empty(request.uri.query))}
- Perform arbitrarily complex arithmetic, such as addition, substraction, division, and multiplication
-
The following expression yields the URI port number incremented by four:
${request.uri.port + 4}
- Perform relational operations, such as numerical equality and inequality
-
The following example is for a DispatchHandler condition, where the request is dispatched if it contains a form field with the attribute
answer
whose value is greater than42
:"bindings": [ { "condition": "#{request.entity.form['answer'] > 42}", "handler": ... } ]
- Perform conditional operations of this form
<condition> ? <if-true> : <if-false>
-
The following example tests whether the request path starts with
/home
. If so, the request is directed tohome
; otherwise, it is directed tonot-home
:${request.uri.path.startsWith('/home') ? 'home' : 'not-home' }
- Consume evaluated configuration tokens described in JSON Evaluation (runtime expressions only)
-
The following example returns
true
if the configuration tokenmy.status.code
resolves to200
:${integer(_token.resolve('my.status.code', '404')) == 200}
Expressions access the environment through the implicit object openig
. The object has the following properties:
-
baseDirectory
, the path to the base location for IG files. The default location is:-
Linux
-
Windows
$HOME/.openig
%appdata%\OpenIG\config
-
-
configDirectory
, the path to the IG configuration files. The default location is:-
Linux
-
Windows
$HOME/.openig/config
%appdata%\OpenIG\config
-
-
temporaryDirectory
, the path to the IG temporary files. The default location is:-
Linux
-
Windows
$HOME/.openig/tmp
%appdata%\OpenIG\OpenIG\tmp
-
To change default values, refer to Change the base location of the IG configuration.
Syntax
Expression syntax must conform to Unified Expression Language described in JSR-245.
Route syntax
Expressions in routes are enclosed in quotation marks, for example:
"${request.method}"
and "#{expression}"
.
Immediate and deferred evaluation syntax
Use the ${}
syntax for expressions to be evaluated at startup or when
a route loads or reloads.
Use the #{}
syntax for expressions to be evaluated later, when the evaluation
result requires the request or response to be fully loaded.
Operator syntax
AM uses the .
and []
operators to access properties:
-
Use
.
for the following tasks:-
Access object properties, such as public fields and Java bean properties.
-
Invoke methods on an object.
-
Access map entries when the specified entry name doesn’t contain reserved characters.
-
-
Use
[]
to access indexed elements:-
If the object is a collection, such as an array, set, or list, use
[i]
to access an element at positioni
. -
If the object isn’t a collection, such as an array, set, or list, use
['prop']
to access the property'prop'
. This is equivalent to.
notation. The following expressions are equivalent:${request.method}
${request['method']}
-
If the object is a map, use
['name']
to access the entry with name'name'
.
-
To access map entries containing characters that are also expression operators,
prevent parsing exceptions by using For example, to access a map field containing a dash ${data['dash-separated-name']} instead of: ${data.dash-separated-name} In the second example, the dash |
Array syntax
The index of an element in an array is expressed as a number in brackets.
For example, the following expression refers to the first Content-Type
header
value in a request:
${request.headers['Content-Type'][0]}
Map syntax
The map key is expressed in brackets. For example, the following expression is an example of Map entry access:
system['prop.name']
If a property doesn’t exist, the index reference yields a null
(empty) value.
Function syntax
Expressions can call built-in functions described in Functions.
Use the syntax ${function(parameter, …)}
, supplying one or more parameters
to the function. For examples, refer to Expressions that use functions.
Functions can be operands for operations and can yield parameters for other function calls.
Method syntax
Use the syntax ${object.method()}
to call a method on the object instance.
If the object resolves to a String, then all methods in the java.lang.String
class are usable.
For examples, refer to Expressions that use functions.
Escape syntax
The character \
is treated as an escape character when followed by
${
or \#{
.
For example, the expression ${true}
normally
evaluates to true
. To include the string ${true}
in an expression,
write \${true}
When \
is followed by any other character sequence, it isn’t treated as an
escape character.
Configuration expressions
IG evaluates configuration expressions at startup and when a route
loads or reloads. Configuration expressions are always evaluated immediately and
use the ${}
syntax.
Configuration expressions can refer to the following information:
-
System heap properties
-
Built-in functions listed in Functions
-
Environment variables,
${env['variable']}
-
System properties,
${system['property']}
Because configuration expressions are evaluated before requests are made,
they can’t refer to the runtime properties request
, response
,
context
, or contexts
.
Runtime expressions
IG evaluates runtime expressions for each request and response, as follows:
- Immediate evaluation of runtime expressions
-
If the expression consumes streamed content, for example, the content of a request or response, IG blocks the executing thread until all the content is available.
Runtime expressions whose evaluation is immediate are written with the
${}
syntax. - Deferred evaluation of runtime expressions
-
IG waits until all streamed content is available before it evaluates the expression. Deferred evaluation doesn’t block executing threads.
Runtime expressions whose evaluation is deferred are written with the
#{}
syntax.When the
streamingEnabled
property inadmin.json
istrue
, expressions that consume streamed content must be written with#
instead of$
.
Runtime expressions can refer to the following information:
-
System heap properties
-
Built-in functions listed in Functions
-
Environment variables
-
System properties
-
attributes
: org.forgerock.services.context.AttributesContext Map<String, Object>, obtained fromAttributesContext.getAttributes()
. For information, refer to AttributesContext. -
context
: org.forgerock.services.context.Context object. -
contexts
: map<string, context> object. For information, refer to Contexts. -
request
: org.forgerock.http.protocol.Request object. For information, refer to Request. -
response
: org.forgerock.http.protocol.Response object, available only when the expression is intended to be evaluated on the response flow. For information, refer to Response. -
session
: org.forgerock.http.session.Session object, available only when the expression is intended to be evaluated for both request and response flow. For information, refer to SessionContext.
Embedded expressions
Consider the following points when embedding expressions:
-
System properties, environment variables, or function expressions can be embedded within expressions.
The following example embeds an environment variable in the argument for a
read()
function. The value ofentity
is set to the contents of the file$HOME/.openig/html/defaultResponse.html
, where$HOME/.openig
is the instance directory:"entity": "${read('&{ig.instance.dir}/html/defaultResponse.html')}"
-
Expressions can’t be embedded inside other expressions, as
${expression}
. -
Embedded elements can’t be enclosed in
${}
.
Extensions
IG offers a plugin interface for extending expressions. See Key extension points.
L-value expressions
L-value expressions assign a value to the expression scope. For example,
"${session.gotoURL}"
assignes a value to the session attribute named gotoURL
.
IG ignores attempts to write to read-only values.
L-value expressions must be specified using immediate evaluation syntax; use
$
instead of #
.
Operators
The following operators are provided by Unified Expression Language:
-
Index property value:
[]
,.
-
Change precedence:
()
-
Arithmetic:
+
(binary),-
(binary),*
,/
,div
,%
,mod
,-
(unary) -
Logical:
and
,&&
,or
,||
,not
,!
-
Relational:
==
,eq
,!=
,ne
,<
,lt
,>
,gt
,⇐
,le
,>=
,ge
-
Empty:
empty
Use this prefix operator to determine whether a value is null or empty. -
Conditional:
?
,:
Operators have the following precedence, from highest to lowest, and from left to right:
-
[]
.
-
()
-
-
(unary)not
!
empty
-
*
/
div
%
mod
-
+
(binary)-
(binary) -
<
>
⇐
>=
lt
gt
le
ge
-
==
!=
eq
ne
-
&&
and
-
||
or
-
?
:
Dynamic bindings
Configuration and runtime expressions can use ExpressionInstant.
The current instant is ${now.epochSeconds}
.
To add or subtract a period of time to the instant, add one or more of the following time periods to the binding:
-
plusMillis(integer)
,minusMillis(integer)
-
plusSeconds(integer)
,minusSeconds(integer)
-
plusMinutes(integer)
,minusMinutes(integer)
-
plusHours(integer)
,minusHours(integer)
-
plusDays(integer)
,minusDays(integer)
The following example binding refers to 30 minutes after the current instant:
${now.plusMinutes(30).epochSeconds}
The following example binding accesses the instant in RFC 1123 date format one day after the current instant:
${now.plusDays(1).rfc1123}
For more examples, refer to the template
property of JwtBuilderFilter
and the attribute-name
property of SetCookieUpdateFilter.
Examples
Immediate evaluation of configuration expressions
The following example yields the value of a secret from a system property.
{
"passwordSecretId": "${system['keypass']}"
}
The following example yields a file from the home directory of the user running the IG application server.
{
"url": "file://${env['HOME']}/keystore.p12"
}
The following example of a temporaryStorage
object takes the value of the
system property storage.ref
, which must a be string equivalent to the name
of an object defined in the heap:
{
"temporaryStorage": "${system['storage.ref']}"
}
Deferred evaluation of runtime expressions
The following example is a Route condition, where the Route is accessed
if the request contains json with the attribute answer
, whose value is 42
.
IG defers evaluation of the expression until it receives the
entire body of the reqest, transfoms it to JSON view, and then introspects it for
the attribute answer
.
{
"condition": "#{request.entity.json['answer'] == 42}",
"handler": ...
}
The following example expression is for a JwtBuilderFilter that uses the content of the request mapped as a string. IG defers evaluation of the expression until it receives the entire body of the request:
{
"template": {
"content": "#{request.entity.string}"
}
}
Immediate and deferred evaluation of runtime expressions
The following example expressions are for an AssignmentFilter that consumes an ID captured from a response.
IG evaluates the first expression immediately to define the target.
It then defers evaluation of the second expression until it receives the entire body of the response.
"onResponse": [
{
"target": "${response.headers['X-IG-FooBar']}",
"value": "#{toString(response.entity.json['userId'])}"
}
]
Expressions that use functions
In the following example, "timer"
is defined by an expression that recovers
the environment variable "ENABLE_TIMER"
and transforms it into a boolean.
Similarly, "numberOfRequests"
is defined by an expression that recovers the
system property "requestsPerSecond"
and transforms it into an integer:
{
"name": "throttle-simple-expressions1",
"timer": "${bool(env['ENABLE_TIMER'])}",
"baseURI": "http://app.example.com:8081",
"condition": "${find(request.uri.path, '^/home/throttle-simple-expressions1')}",
"handler": {
"type": "Chain",
"config": {
"filters": [
{
"type": "ThrottlingFilter",
"name": "ThrottlingFilter-1",
"config": {
"requestGroupingPolicy": "",
"rate": {
"numberOfRequests": "${integer(system['requestsPerSecond'])}",
"duration": "10 s"
}
}
}
],
"handler": "ReverseProxyHandler"
}
}
}
If "requestsPerSecond"=6
and "ENABLE_TIMER"=true
, after the expressions
are evaluated IG views the example route as follows:
{
"name": "throttle-simple-expressions2",
"timer": true,
"baseURI": "http://app.example.com:8081",
"condition": "${find(request.uri.path, '^/home/throttle-simple-expressions2')}",
"handler": {
"type": "Chain",
"config": {
"filters": [
{
"type": "ThrottlingFilter",
"name": "ThrottlingFilter-1",
"config": {
"requestGroupingPolicy": "",
"rate": {
"numberOfRequests": 6,
"duration": "10 s"
}
}
}
],
"handler": "ReverseProxyHandler"
}
}
}