Fluent Commerce Logo
Docs
Sign In

Exception Management in writing Rules

Essential knowledge

Author:

Fluent Commerce staff

Changed on:

30 June 2024

Overview

The Workflow Framework Engine ensures that all Exceptions thrown out of Rules are recorded within the Orchestration Audit Events, accessible via the Event API. This is important for providing detailed information about what went wrong.

Key points

  • To ensure that the Orchestration Audit Events contain as much useful information as possible, it is important to consider the Exception Strategy used by your Rules.
  • Don't swallow exceptions inside Rules, and always allow a caught exception to be re-thrown, or added as a cause to a new exception to ensure the cause is included in the Audit Events.
  • The Workflow Framework provides a special exception type called RuleExecutionException which provides special handling of exceptions differently from all others.

To ensure that the Orchestration Audit Events contain as much useful information as possible, it is important to consider the Exception Strategy used by your Rules.

Exceptions are handled by the Workflow Engine in two ways:

  • Special handling for exceptions of type 
    `RuleExecutionException`
  • Default handling for all other exceptions

We typically don't recommend using try-catch blocks within Rules, and if any exceptions are thrown from the code to allow them to bubble through.  However, there may be some specific scenarios where you would like to throw an exception from within your rules. In this case, make sure to always include the cause exception in the throw:

1public class ExampleRule implements Rule {
2
3    public <C extends Context> void run(C context) {
4
5        try {
6            // some rule code...
7        }
8        catch (Exception e) {
9            throw new CustomRuleException("Useful Message", e); // custom exception including the cause exception
10        }
11    } 
12}

Language: java

Name: Example

Description:

[Warning: empty required content area]

The RuleExecutionException

The Workflow Framework provides a special exception type called 

`RuleExecutionException`
 which provides special handling of exceptions differently from all others. Any 
`RuleExecutionException`
, or subclass thereof, thrown from or bubbled up through a rule back into the Workflow Engine Executor, will be handled as follows:

  • Rubix will stop the execution of the current Ruleset, but continue processing previously queued inline events
  • Rubix will not process any queued actions
  • Rubix will log the exception in an orchestration audit Event, without a stack trace
  • Rubix will mark the Event as success
  • Rubix will return a success response to the UI if the execution was triggered by a User Action
  • Rubix will create and attempt to execute a Ruleset Exception Event
    • The name of this new Ruleset Exception Event is the same as the Ruleset that threw the exception and the 
      `eventType`
       is set to 
      `EXCEPTION`
    • The 
      `RuleExecutionEvent`
       message and the 
      `cause`
       throwable (if it exists), will be available as part of the Exception Event for use within your rules
1"rulesets": [
2  {
3    "name": "CancelOrder",
4    "type": "ORDER",
5    "subtype": "CC",
6    "eventType": "NORMAL",
7    "rules": [ ... ],
8    "triggers": [ ... ],
9    "userActions": []
10  },
11  {
12    "name": "CancelOrder",
13    "type": "ORDER",
14    "subtype": "CC",
15    "eventType": "EXCEPTION",
16    "rules": [ ... ],
17    "triggers": [ ... ],
18    "userActions": []
19  }
20]

Language: json

Name: Example Ruleset and EXCEPTION Ruleset:

Description:

[Warning: empty required content area]
1{
2  "id": "8dcedfd0-a767-4066-8622-574f07cf092a",
3  "name": "ACME.custom2023.ThrowRuleExecutionExceptionRule",
4  "type": "ORCHESTRATION_AUDIT",
5  "accountId": "ACME",
6  "retailerId": "1",
7  "category": "rule",
8  "context": {
9    "sourceEvents": [
10      "e5de6a3b-0653-4df4-bd3d-ca3a83b1fcea"
11    ],
12    "entityType": "ORDER",
13    "entityId": "794",
14    "entityRef": "CC_4137",
15    "rootEntityType": "ORDER",
16    "rootEntityId": "794",
17    "rootEntityRef": "CC_4137"
18  },
19  "eventStatus": "FAILED",
20  "attributes": [
21    {
22      "name": "ruleSet",
23      "value": "TestExceptions",
24      "type": "STRING"
25    },
26    {
27      "name": "props",
28      "value": {},
29      "type": "STRING"
30    },
31    {
32      "name": "startTimer",
33      "value": 1689567378049,
34      "type": "STRING"
35    },
36    {
37      "name": "message",
38      "value": "Example of RuleExecutionException thrown from Rule",
39      "type": "STRING"
40    },
41    {
42      "name": "stopTimer",
43      "value": 1689567378059,
44      "type": "STRING"
45    }
46  ],
47  "source": null,
48  "generatedBy": "Rubix User",
49  "generatedOn": "2023-07-17T04:16:18.059+00:00"
50}

Language: json

Name: Example Orchestration Audit Event for RuleExecutionException:

Description:

[Warning: empty required content area]

All Other Exceptions

For all other exceptions thrown or bubbled through from Rules, the Workflow Framework Engine will handle these as follows:

  • Rubix will stop the current execution
  • Rubix will not process any queued actions
  • Rubix will log the exception in an orchestration audit Event, including a stack trace
  • Rubix will mark the Event as failed
  • Rubix will return an error response to the UI if the execution was triggered by a User Action
  • Rubix will not create and attempt to execute a Ruleset Exception Event

Example Orchestration Audit Event for other Exceptions:

1{
2  "id": "39ffb549-6e82-4e89-bc24-b1f2ec839e49",
3  "name": "java.lang.IllegalArgumentException",
4  "type": "ORCHESTRATION_AUDIT",
5  "accountId": "ACME",
6  "retailerId": "1",
7  "category": "exception",
8  "context": {
9    "sourceEvents": [
10      "48ef5c4e-8f29-4de0-9249-90f2c8cb5ae7"
11    ],
12    "entityType": "ORDER",
13    "entityId": "827",
14    "entityRef": "CC_5369",
15    "rootEntityType": "ORDER",
16    "rootEntityId": "827",
17    "rootEntityRef": "CC_5369"
18  },
19  "eventStatus": "FAILED",
20  "attributes": [
21    {
22      "name": "exception",
23      "value": {
24        "message": "Example of an Exception thrown from a Rule",
25        "stackTrace": [
26          {
27            "fileName": "ThrowOtherExceptionRule.java",
28            "className": "com.fluentcommerce.rule.ThrowOtherExceptionRule",
29            "lineNumber": 11,
30            "methodName": "run",
31            "nativeMethod": false,
32            "declaringClass": "com.fluentcommerce.rule.ThrowOtherExceptionRule"
33          },
34          // LOTS MORE STACKTRACE...
35          {
36            "fileName": "Thread.java",
37            "className": "java.lang.Thread",
38            "lineNumber": 750,
39            "methodName": "run",
40            "nativeMethod": false,
41            "declaringClass": "java.lang.Thread"
42          }
43        ],
44        "suppressed": [],
45        "classContext": [
46          "com.fluentcommerce.rule.ThrowOtherExceptionRule",
47          "com.fluentretail.rubix.plugin.registry.impl.BaseRuleProxyFactory$1",
48          "com.sun.proxy.$Proxy72",
49          "com.fluentretail.rubix.executor.EventExecutor",
50          "com.fluentretail.rubix.executor.EventExecutor",
51          "com.fluentretail.rubix.executor.EventExecutor",
52          "com.fluentretail.rubix.executor.EventExecutor",
53          "com.fluentretail.rubix.executor.EventExecutor$$Lambda$136/403856380",
54          "java.util.stream.MatchOps$1MatchSink",
55          "java.util.ArrayList$ArrayListSpliterator",
56          "java.util.stream.ReferencePipeline",
57          "java.util.stream.AbstractPipeline",
58          "java.util.stream.AbstractPipeline",
59          "java.util.stream.AbstractPipeline",
60          "java.util.stream.MatchOps$MatchOp",
61          "java.util.stream.MatchOps$MatchOp",
62          "java.util.stream.AbstractPipeline",
63          "java.util.stream.ReferencePipeline",
64          "com.fluentretail.rubix.executor.EventExecutor",
65          "com.fluentretail.rubix.executor.EventExecutor",
66          "com.fluentretail.rubix.executor.EventExecutor",
67          "com.fluentretail.rubix.executor.EventExecutor",
68          "com.fluentretail.rubix.executor.RubixEventHandler",
69          "com.fluentretail.rubix.executor.RubixEventHandler",
70          "org.apache.felix.ipojo.util.Callback",
71          "org.apache.felix.ipojo.handlers.event.subscriber.EventAdminSubscriberHandler",
72          "org.apache.felix.ipojo.handlers.event.subscriber.EventAdminSubscriberHandler",
73          "org.apache.felix.eventadmin.impl.handler.EventHandlerProxy",
74          "org.apache.felix.eventadmin.impl.tasks.HandlerTask",
75          "org.apache.felix.eventadmin.impl.tasks.SyncDeliverTasks",
76          "org.apache.felix.eventadmin.impl.handler.EventAdminImpl",
77          "org.apache.felix.eventadmin.impl.security.EventAdminSecurityDecorator",
78          "com.fluentretail.rubix.queue.EventActivator$1",
79          "com.fluentretail.rubix.queue.EventActivator$1$$Lambda$108/390688491",
80          "com.fluentretail.rubix.queue.impl.QueueListener",
81          "com.amazon.sqs.javamessaging.SQSSessionCallbackScheduler",
82          "java.util.concurrent.ThreadPoolExecutor",
83          "java.util.concurrent.ThreadPoolExecutor$Worker",
84          "java.lang.Thread"
85        ],
86        "detailMessage": "Example of an Exception thrown from a Rule",
87        "localizedMessage": "Example of an Exception thrown from a Rule",
88        "suppressedExceptions": []
89      },
90      "type": "OBJECT"
91    },
92    {
93      "name": "lastRule",
94      "value": "ACME.custom2023.ThrowOtherExceptionRule",
95      "type": "String"
96    },
97    {
98      "name": "lastRuleSet",
99      "value": "TestExceptions",
100      "type": "String"
101    },
102    {
103      "name": "message",
104      "value": "Example of an Exception thrown from a Rule",
105      "type": "String"
106    }
107  ],
108  "source": null,
109  "generatedBy": "Rubix User",
110  "generatedOn": "2023-07-17T04:21:53.439+00:00"
111}

Language: json

Name: Example Orchestration Audit Event for other Exceptions:

Description:

[Warning: empty required content area]
Fluent Commerce staff

Fluent Commerce staff

Copyright © 2024 Fluent Retail Pty Ltd (trading as Fluent Commerce). All rights reserved. No materials on this docs.fluentcommerce.com site may be used in any way and/or for any purpose without prior written authorisation from Fluent Commerce. Current customers and partners shall use these materials strictly in accordance with the terms and conditions of their written agreements with Fluent Commerce or its affiliates.

Fluent Logo