Groovy DSL Rules
- 1 Overview
- 2 General Groovy DSL rule structure
- 2.1.1 rules.groovy
- 2.2 Or/And conditions
- 2.2.1 And
- 2.2.1.1 rules.groovy
- 2.2.2 Or
- 2.2.2.1 rules.groovy
- 2.2.3 Default
- 2.2.1 And
- 2.3 Unconditional routing rule
- 2.3.1 rules.groovy
- 2.3.2 rules.groovy
- 2.4 Message rule
- 2.4.1.1 rules.groovy
- 2.4.2 Source section
- 2.4.2.1 Endpoint parameters
- 2.4.2.1.1 rules.groovy
- 2.4.2.2 Session
- 2.4.2.2.1 rules.groovy
- 2.4.2.3 Groups
- 2.4.2.3.1 rules.groovy
- 2.4.2.4 Available destinations
- 2.4.2.4.1 rules.groovy
- 2.4.2.5 Custom
- 2.4.2.5.1 rules.groovy
- 2.4.2.5.2 rules.groovy
- 2.4.2.1 Endpoint parameters
- 2.4.3 Condition section
- 2.4.3.1 Field content
- 2.4.3.1.1 Single construct
- 2.4.3.1.2 rules.groovy
- 2.4.3.1.3 rules.groovy
- 2.4.3.1.4 Group construct
- 2.4.3.1.5 rules.groovy
- 2.4.3.2 Header content
- 2.4.3.2.1 rules.groovy
- 2.4.3.3 Message type
- 2.4.3.3.1 rules.groovy
- 2.4.3.3.2 rules.groovy
- 2.4.3.4 Field is today
- 2.4.3.4.1 rules.groovy
- 2.4.3.4.2 rules.groovy
- 2.4.3.5 Field(s) exist
- 2.4.3.6 Field(s) not exist
- 2.4.3.6.1 rules.groovy
- 2.4.3.6.2 rules.groovy
- 2.4.3.7 Repeating group(s) existence
- 2.4.3.8 Destination(s) exist by ID
- 2.4.3.8.1 rules.groovy
- 2.4.3.8.2 rules.groovy
- 2.4.3.9 Destination(s) not exist by ID
- 2.4.3.9.1 rules.groovy
- 2.4.3.9.2 rules.groovy
- 2.4.3.10 Message match
- 2.4.3.10.1 rules.groovy
- 2.4.3.10.2 rules.groovy
- 2.4.3.11 Message mismatch
- 2.4.3.11.1 rules.groovy
- 2.4.3.11.2 rules.groovy
- 2.4.3.12 Custom
- 2.4.3.12.1 rules.groovy
- 2.4.3.12.2 rules.groovy
- 2.4.3.1 Field content
- 2.4.4 Action section
- 2.4.4.1 Send to destination(s) by ID
- 2.4.4.1.1 rules.groovy
- 2.4.4.1.2 rules.groovy
- 2.4.4.2 Send by repeating groups to destination ID
- 2.4.4.2.1 rules.groovy
- 2.4.4.2.2 rules.groovy
- 2.4.4.3 Send by groups
- 2.4.4.3.1 rules.groovy
- 2.4.4.3.2 rules.groovy
- 2.4.4.4 Send by TargetIDs
- 2.4.4.4.1 rules.groovy
- 2.4.4.4.2 rules.groovy
- 2.4.4.5 Reject message
- 2.4.4.5.1 rules.groovy
- 2.4.4.5.2 rules.groovy
- 2.4.4.6 Modify fields
- 2.4.4.6.1 rules.groovy
- 2.4.4.6.2 rules.groovy
- 2.4.4.7 Modify header
- 2.4.4.7.1 rules.groovy
- 2.4.4.7.2 rules.groovy
- 2.4.4.8 If condition
- 2.4.4.8.1 rules.groovy
- 2.4.4.8.2 rules.groovy
- 2.4.4.8.3 rules.groovy
- 2.4.4.8.4 rules.groovy
- 2.4.4.9 Route by field value
- 2.4.4.9.1 rules.groovy
- 2.4.4.9.2 rules.groovy
- 2.4.4.10 Custom
- 2.4.4.10.1 rules.groovy
- 2.4.4.10.2 rules.groovy
- 2.4.4.11 Context exit
- 2.4.4.11.1 rules.groovy
- 2.4.4.12 Repeating group
- 2.4.4.1 Send to destination(s) by ID
- 2.5 Event rule
- 2.5.1.1 rules.groovy
- 2.5.2 Description section
- 2.5.3 eventType section
- 2.5.3.1 rules.groovy
- 2.5.3.2 rules.groovy
- 2.5.3.3 rules.groovy
- 2.5.3.4 rules.groovy
- 2.5.4 Condition section
- 2.5.4.1 FixSessionStateEventRule
- 2.5.4.1.1 rules.groovy
- 2.5.4.1.2 rules.groovy
- 2.5.4.2 NewSessionEventRule
- 2.5.4.2.1 rules.groovy
- 2.5.4.3 ErrorEventRule
- 2.5.4.3.1 rules.groovy
- 2.5.4.4 ScheduleEndpointEventRule
- 2.5.4.4.1 rules.groovy
- 2.5.4.5 SchedulerEventRule
- 2.5.4.5.1 rules.groovy
- 2.5.4.6 SnFEventRule
- 2.5.4.6.1 rules.groovy
- 2.5.4.7 UnprocessedMessageEventRule
- 2.5.4.7.1 rules.groovy
- 2.5.4.8 Custom
- 2.5.4.8.1 rules.groovy
- 2.5.4.8.2 rules.groovy
- 2.5.4.1 FixSessionStateEventRule
- 2.5.5 Action section
- 2.5.5.1 Custom
- 2.5.5.1.1 rules.groovy
- 2.5.5.1.2 rules.groovy
- 2.5.5.1 Custom
Overview
The ability to create Groovy DSL rules is available since FIXEdge Java 1.10.3 release.
The FIXICC H2 Business Layer Components have supported Groovy DSL rules since version 1.1.0.
Groovy DSL (Domain Specific Language) allows non-programmers to intuitively author and maintain business rules for FIXEdge Java in a much simpler way compared to programming in pure Groovy or Java.
Groovy DSL covers most use cases in FIX protocol-defined workflows: routing to endpoints, FIX message transformation, and handling of important session management events. It supports both a declarative style of writing rules for unconditional routing “from <endpoint A> to <endpoint B>” and an imperative style where DSL conditional logic and executable actions are expressed explicitly for a given session and selected set of fields. The DSL message rules are defined by the description and bound for the processing of incoming messages from a particular session or group of sessions.
The DSL-defined business rules are stored in a way similar to the rules expressed in Groovy or Java. The rules.groovy configuration file allows mixing and matching DSL with pure Groovy and Java code for backward compatibility and complex cases with message enrichment. The processing of DSL-expressed rules is as efficient as Groovy and Java written rules.
General Groovy DSL rule structure
Groovy DSL rules can be configured in the rules.groovy configuration file which also remains compatible with the pure Java and Groovy implementation of rules.
There are three optional blocks in the rulesDSL structure:
messageRules for message rules
eventRules for event rules
routing for unconditional routing rules
Groovy DSL rules must be grouped in these blocks respectively.
General Groovy DSL rule structure:
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule(description) {...}
messageRule(description) {...}
}
eventRules {
eventRule(description) {...}
eventRule(description) {...}
}
routing {
from "sourceId" to "destinationId"
}
}Imported classes are described in the sysconf/fej-routing.xml file.
Or/And conditions
Logical And/Or blocks can be used inside conditional blocks (messageRule: source, condition; eventRule: condition).
And
And condition means that instructions will be executed sequentially. If one is false, then the others are not executed and the entire expression returns 'false'. If all are true, then the entire expression returns 'true' also.
Syntax:
rules.groovy
and {
instruction1
instruction2
}Or
Or condition means that instructions will be sequential. If one is true, then the others are not executed and the entire expression returns 'true'. If all are false, then the entire expression returns 'false' also.
Syntax:
rules.groovy
or {
instruction1
instruction2
}Default
If no logical block is specified inside the conditional block then the instructions will be connected via And by default.
The routing block is intended to route all messages between two endpoints unconditionally.
Syntax:
rules.groovy
routing {
from <source ids> to <destination ids>
...
from <source ids> to <destination ids>
}<source ids> list represents the list of source session IDs.
<destination ids> list represents the list of destination session IDs.
Example:
FIXEdge Java must send all messages from 'session2' and 'session4' sessions to 'session3', 'session2, and 'session4' destination sessions. And send all received messages from the 'session3' session to the 'session4' destination session.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
routing {
from "session2", "session4" to "session3", "session2", "session4"
from "session3" to "session4"
}
}messageRule block is intended to build a rule for message processing. It can contain instructions for:
Filtering of incoming and outgoing FIX messages
Transformation of incoming and outgoing FIX messages
Routing of incoming and outgoing FIX messages
It is also possible to route one message to multiple destinations or to the same destination multiple times.
More than one rule from the messageRule block can be applied to the message. Groovy DSL message rules will be applied in the order set in the rules.groovy configuration file.
Syntax:
rules.groovy
messageRule(description){
source {
//instructions, optional
}
condition {
//instructions, optional
}
action {
//instructions
}
}Each messageRule block contains 4 sections:
Section | Description |
|---|---|
Description | Mandatory section. This section provides a string with a free text - description of the business rule. |
Source | Optional section. This section defines instructions for primary message filtering. The source section defines a source from which the FIX message can come. If the message source coincides with the source defined in the Source section then the rule will be applied to the message. If the Source section is not specified then the rule will be applied to FIX messages from all sources. This filter can be applied on a static basis without additional effect at runtime. The Source section is the SourceCondition implementation |
Condition | Optional section. This section contains a set of pre-defined criteria. The application checks FIX messages on specified criteria and if it is met the instructions will be executed. If the Condition section is not specified then all messages will be maintained in accordance with the Action section. The Condition section is the RuleCondition implementation. |
Action | Mandatory section. This section defines the instructions that must be performed when the BL rule is being applied to a message. The Action section must contain at least one instruction. The Action section is the RuleAction implementation. |
The source from which the FIX message can come for processing may be defined by endpoint parameters:
id <regex>
targetCompId <regex>
targetSubId <regex>
targetLocationId <regex>
senderCompId <regex>
senderSubId <regex>
senderLocationId <regex>
<regex> means that the regular expression or the full name of the parameter can be specified.
Example:
The rule will be applied to the messages, which sources meet the following criteria: session ID must start with session' and the targetCompId is 'TargetCompID1' or the senderCompId contains 'Sender'.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Filter source by endpoint parameters") {
source{
id "session*"
or {
targetCompId "TargetCompID1"
senderCompId ".*Sender.*"
}
}
}
}
}Session
The source from which the FIX message can come for processing may be defined by the session parameters. Works similarly to the previous one, but is used for visual separation and grouping (e.g. per session).
Example:
The rule will be applied to the messages, which sources meet the following criteria: session senderCompId contains 'Sender' and either session id is 'session1' and targetCompId is 'TargetCompID1' or session id is 'session2' and targetCompId 'TargetCompID2'.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Filter source by endpoint parameters") {
source{
senderCompId ".*Sender.*"
or{
session {
id "session1"
targetCompId "TargetCompID1"
}
session {
id "session2"
targetCompId "TargetCompID2"
}
}
}
}
}
}Groups
The source from which the FIX message can come may be defined by the resource group. Logical conditions can be used inside.
Instruction | Description |
|---|---|
contains <groups list> | The source is all groups from the proposed list. <groups list> - string list with the group names. Example: groups {
contains "A", "B", "C"
} |
contains <filter action> from <groups list> | The source is the group(s) from the list which satisfies the <filter action>. <filter action> may be set to one of the following values: any | all | none. <groups list> - string list with the group names. Example: groups {
contains any from "A", "B", "C"
} |
size <num> | The source is the group(s) with the specified size. <num> - integer, the size of the group. Example: groups {
size 5
} |
size_more <num> | The source is the group(s) which size is more than the specified value. <num> - integer, the minimum size of the group. Example: groups {
size_more 5
} |
size_less <num> | The source is the group(s) which size is less than the specified value. <num> - integer, the maximum size of the group. Example: groups {
size_less 5
} |
Example:
The rule will be applied to the messages, which sources meet the following criteria: the source contains all 'A', 'B', and 'C' groups and the group size is 5, or the group list contains one of the 'B', D' groups and the group size is more than 3 and less than 6.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Filter source by groups") {
source {
or {
groups {
contains "A", "B", "C"
size 5
}
groups {
contains any from "B", "D"
size_less 6
size_more 3
}
}
}
}
}
}Available destinations
The source from which the FIX message can come may be defined by the destination endpoints contained in specified Groups.
Instruction | Description |
|---|---|
size <num> | The source is the group(s) for which the number of available destinations equals <num>. <num> - integer, the number of available destinations for the group. Example: available_destinations {
size 4
} |
size_more <num> | The source is the group(s) for which the number of available destinations is more than <num>. <num> - integer, the minimum number of available destinations for the group. Example: available_destinations {
size_more 2
} |
size_less <num> | The source is the group(s) for which the number of available destinations is less than <num>. <num> - integer, the maximum number of available destinations for the group. Example: available_destinations {
size_less 8
} |
quantity <num> by_groups <groups list> | The source is the group(s) from the list for which the number of available destinations equals <num>. <groups list> - string list with the group names. At least one group from the list must belong to the endpoint. Example: available_destinations {
quantity 4 by_groups "A", "B", "C"
} |
quantity <comparator> than <num> by_groups <groups list> | The source is the group(s) from the list for which the number of available destinations satisfies the <comparator>. <comparator> - may be set to one of the following values: more | less. <groups list> - string list with the group names. At least one group from the list must belong to the endpoint. Example: available_destinations {
quantity more than 4 by_groups "A", "B", "C"
} |
Example:
The rule will be applied to the messages, which sources meet the following criteria: the number of available destination endpoints by the group is 4, or the number of available resources received by the provided groups 'A', 'B', and 'C' is less than 8.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Filter source by number of available destinations ") {
source {
or {
available_destinations {
size 4
}
available_destinations {
quantity less than 4 by_groups "A", "B","C"
}
}
}
}
}
}Custom
If there is a need to write customized source conditions, FIXEdge Java provides the ability to use the Custom block to describe the required behavior. For example, for the customized endpoint this block can be combined with all the previous ones, and several of them can be used.
If any external classes are used, they must be imported.
Syntax:
rules.groovy
source {custom {source → <some actions that will return a boolean value in the end> }}Example:
The rule will be applied to the messages, which source meets the following criteria: the session id is 'session1' or the group size equals 5, and the number of available destinations received through the groups of this resource is greater than or equal to 4.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Filter source by number of available destinations, endpoint id and groups size, using custom implementation") {
source {
or {
id "session1"
groups { size 5 }
}
custom { source ->
Set<String> groups = source.getGroups()
routingContext.getAvailableDestinationsByGroup(groups).size() >= 4
}
}
}
}
}The message received from the selected Source can be dynamically filtered by:
field content (single or group conditions)
header content
message type
current date and time
existence of the field(s)
non-existence of the field(s)
existence of the destination(s)
non-existence of the destination(s)
message(s) match
message(s) mismatch
custom condition
Field content
It is possible to filter messages from the selected Source both by a single field value and a group of values for the same field.
Single construct
For a single construct, it is enough to specify the field number and one condition against which the field should be checked.
Syntax:
rules.groovy
field <tagNum> <condition>where <tagNum> is an integer, field number and <condition> is a condition for checking the value of the field.
The list of available conditions:
Condition | Description |
|---|---|
greater <num> | The value of the field is greater than <num>. <num> - integer or double. Example: condition {
field 34 greater 2
} |
greater_or_equal <num> | The value of the field is greater or equal to <num>. <num> - integer or double. Example: condition {
field 34 greater_or_equal 2
} |
less <num> | The value of the field is less than <num>. <num> - integer or double. Example: condition {
field 34 less 2
} |
less_or_equal <num> | The value of the field is less or equal to <num>. <num> - integer or double. Example: condition {
field 34 less_or_equal 2
} |
value <value> | The value of the field equals to the <value>. <value> - integer or double or string (regex). Example: condition {
field 58 value "TestText"
} |
length <num> | The length of the field is equal to the <num>. <num> - integer. Example: condition {
field 58 length 5
} |
length <comparator>than <num> | The length of the field is compared to <num>. <num> - integer. <comparator> - 'greater' or 'greater_or_equal' or 'less or less_or_equal'. Example: condition {
field 58 length greater than 5
} |
contains <string list> | The tag value contains all elements from the <string list>. <string list> - string, list of values. Example: condition {
field 58 contains "Test", "Text"
} |
from <string list> contains <action> | The tag value contains <action> elements from the <string list>. <string list> - string, list of values. <action> - 'all' or 'any' or 'none'. Example: condition {
field 58 from "Test", "Text" contains none
} |
begins_with <prefix> | The tag value begins with <prefix>. <prefix> - string. Example: condition {
field 58 begins_with "T"
} |
Example:
The received messages are passed to the Condition section. The filtering condition is the following: field 35 equals "D", the value of the 38 field is less than 5 and greater than 2, and 58 field begins with the "Test" and has a length greater than 2.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Condition to check message type") {
field 35 value "D"
field 38 less 5
field 38 greater 2
or{
field 58 begins_with "Test"
field 58 length greater than 2
}
}
}
}Group construct
For a group construct, the field number and a list of conditions to be applied to it must be specified.
Syntax:
rules.groovy
field ( <num> ) {
<condition 1>
<condition 2>
. . .
<condition n>
}The list of available conditions:
Condition | Description |
|---|---|
value <comparator> than <num> | The value of the field is compared to <num>. <num> - integer or double. <comparator> - 'greater' or 'greater_or_equal' or 'less' or 'less_or_equal'. Example: condition {
field (34) {
greater 2
less 10
}
} |
value <value> | The value of the field equals to the <value>. <value> - integer or double or string (regex). Example: condition {
field (58){
or{
value "Test.*"
value 5
value 1.3
}
}
} |
length <num> | The length of the field equals to the <num>. <num> - integer. Example: condition {
field (58) {
length 5
}
} |
length <comparator> than <num> | The length of the field is compared to <num>. <num> - integer. <comparator> - 'greater' or 'greater_or_equal' or 'less' or 'less_or_equal'. Example: condition {
field (58) {
length greater than 5
length less than 25
}
} |
contains <string list> | The value of the field contains all elements from the <string list>. <string list> - string, list of values. Example: condition {
field (58){
contains "Test", "Text"
}
} |
contains <action> from <string list> | The tag value contains <action> elements from the <string list>. <string list> - string, list of values. <action> - 'all' or 'any' or 'none'. Example: condition {
field (58){
contains any from "Test", "Text"
}
} |
begins_with <prefix> | The value of the field begins with <prefix>. <prefix> - string. Example: condition {
field (58) {
begins_with "T"
}
} |
Header content
It is possible to filter messages from the selected Source by header content. Logical conditions And/Or can be applied.
Syntax:
rules.groovy
header {
<header condition 1>
. . .
<header condition n>
}Header Condition | Description |
|---|---|
field (<name>) {<field condition>} | The value of the header is checked against <name>. More details about the <field condition> is below. The same as field content with group construct. <name> - string, name of the header property. <field condition> - condition to check header content by name. Example: condition {
header {
field("FieldName") { value "SampleValue" }
}
} |
exist <names> | The condition returns 'true' if all fields with given <names> exist. <names> - string, the list of header names. Example: condition {
header {exist "FieldName1", "FieldName2"}
} |
exist <action> from <names> | The <action> header properties from the <names> list exist. <names> - string, list of header names. <action> - 'all' or 'any' or 'none'. Example: condition {
header {exist any from "FieldName1", "FieldName2"}
} |
not_exist <names> | The condition returns 'true' if all fields from the <names> list do not exist. <names> - string, the list of header names. Example: condition {
header { not_exist "FieldName1", "FieldName2"}
} |
Message type
It is possible to filter messages from the selected Source by message type.
Syntax:
rules.groovy
condition {
msgType <list of msg types>
}Example:
The received messages are passed to the Condition section. The filtering condition is the following: the message type must be equal to one of the values in the provided list: "D", 8, "AE".
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Condition to check message type") {
condition {
msgType "D", 8, "AE"
}
}
}
}Field is today
It is possible to filter messages from the selected Source by checking whether the value of the date/time field is the current date. The condition returns 'false' if the field value has an incorrect format or is not the current day.
Syntax:
rules.groovy
condition {
is_today <tagNum>
}The is_today condition must be used with the fields with a time format of value, for example, 52 or 60.
Example:
The received messages are passed to the Condition section. The filtering condition is the following: the value of the 52 filed is today's date.
rules.groovy
import com.epam.fej.routing.RoutingContext
import static dsl.CommonRulesDsl.rulesDSL
rulesDSL(routingContext as RoutingContext) {
messageRules {
messageRule("Condition to check message type") {
condition {
msgType "D", 8, "AE"
}
}
}
}Field(s) exist
It is possible to filter messages from the selected Source by checking that the field(s) exists in these messages.
Condition | Description |
|---|---|
exist <action> from <tagNums> | The <action> tag numbers from the <tagNums> list exist in the message. <tagNums> - the list of tag numbers. <action> - all or any or none. Example: condition {
exist any from 35, 49, 56
} |
exist <tagNums> | The condition returns 'true' if all fields with tag numbers from the <tagNums> list exist. <tagNums> - the list of tag numbers. Example: |