Exception Management in writing Rules
Author:
Fluent Commerce
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`
`RuleExecutionException`
- 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 is set to
`eventType`
`EXCEPTION`
- The message and the
`RuleExecutionEvent`
throwable (if it exists), will be available as part of the Exception Event for use within your rules`cause`
- The name of this new Ruleset Exception Event is the same as the Ruleset that threw the exception and the
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]