Last Updated on 26/07/2022 by Patryk Bandurski
Errors occur all the time. All you can do is implement error handling. In this article, I will describe how to use the introduced abstraction layer in the exceptions area. Before Mule 4, developers only could access raw Java exceptions as Mule is a Java-based ESB. This led to a situation where you, as a developer, needed to analyze documentation to find the exact exception you would like to handle. Now an Error concept was introduced.
Before you dig into the details, check out my latest interactive infographic about error handling in Mule 4. It is available under this link.
Error Object
The developer needs to operate on a raw Java exception in the previous version of Mule. However, in Mule 4 Error concept has been introduced. Below I have prepared a simple UML diagram depicting this new idea.
The main Error class has all information about the exception, like description and reference to the original Java object that inherits from the Throwable interface. To differentiate what error we are coupling with, we can use the errorType field. It contains an error namespace and identifier. Let’s see how Error looks for 404 error. The representation below is in JSON form.
{
"description": "HTTP GET on resource 'http://localhost:8091/notfound' failed: not found (404).",
"detailedDescription": "HTTP GET on resource 'http://localhost:8091/notfound' failed: not found (404).",
"errorType": {
"namespace": "HTTP",
"identifier": "NOT_FOUND"
},
"cause": "org.mule.extension.http.api.request.validator.ResponseValidatorTypedException"
}
As you can see errorType holds HTTP:NOT_FOUND type. This informs us that we are handling HTTP error and specifically NOT_FOUND one. We can access meaningful descriptions in the description and detailedDescription fields.
The most important question is: how do I access errors from dataweave? You do this by using error
keyword.
Hierarchy of errors
All errors are in a hierarchical structure like below.
We have ANY error that can match any error. It is the root of the hierarchy. We have one error known as UNKNOWN that is returned in cases when the cause is not known. All errors that inherit from ANY can be handled in Try Scope. In contrast, CRITICAL can not be handled. These exceptions cause applications to stop like storage size has been exceeded or out of memory.
In the previous section, I wrote that error also has a namespace. It has not been depicted on the diagram as all of these errors can occur for different namespaces. Let’s say that we have an HTTP Request Connector. It can have connectivity issues. In such a case, we should receive HTTP:CONNECTIVITY not CONNECTIVITY. You need to look following pair namespace and identifier as a whole.
Error Handling
We have two strategies. It is much simpler than before. We can catch the error and handle it, or we can rethrow it. We have also Try Scope to use it with. This scope encapsulates one or more event processors and handles this piece of flow errors.
On Error Propagate
This strategy you can use when you want to rethrow an error after the error handling login has been completed. You can define a couple of properties on this element:
- enableNotifications – boolean flag, if set to true Mule engine will fire ExceptionNotification,
- logException – boolean flag, if set to true the Mule engine will log the exception,
- type – error type (namespace + identifier) linked with error handler,
- when – complex condition, if evaluated to true Mule engine will handle error in this error handler.
Below you can find a simple Try Scope around the Request Listener Connector. In the visual editor, you can see what condition does error handler expects. Here is the exact match of errorType
. This strategy will be triggered only for NOT_FOUND error in the HTTP context.
Below is the XML representation of the flow above. Both enableNotification and logException have default values set to true. You can place within on-error-propagate as many event processors as you like. In the given example payload is set and is logged.
<error-handler>
<on-error-propagate enableNotifications="true" logException="true" doc:name="On Error Propagate" type="HTTP:NOT_FOUND">
<ee:transform doc:name="Error Response">
<ee:message>
<ee:set-payload>
<![CDATA[%dw 2.0
output application/json
---
{
prop: "Super Value"
}]]>
</ee:set-payload>
</ee:message>
</ee:transform>
<logger level="INFO" doc:name="Log the Message"/>
</on-error-propagate>
</error-handler>
When the HTTP Request Connector throws a Not Found error, it will trigger the error handler. The caught exception will be logged and the payload would be set to a new value. Then the error will be rethrown to the next scope. Like in the diagram below, caught exceptions in private-try-flow will be rethrown and sent to main-try-flow. In this case, two possible outcomes are possible. If error handling is in place, Mule will route the error there. If not, the error will be treated as an unhandled error caught in main-try-flow.
On Error Continue
This strategy you can use when you want to handle an error and process further flow as if nothing has happened. You can define a couple of properties on this element:
- enableNotifications – boolean flag, if set to true Mule engine will fire ExceptionNotification,
- logException – boolean flag, if set to true the Mule engine will log an exception,
- type – error type (namespace + identifier) linked with error handler,
- when – complex condition, if evaluated to true Mule engine will handle error in this error handler.
Below you can find a simple Try Scope around Transform Message. In the visual editor, you can see what condition does error handler expects. This one expects ANY error as nothing more than “On Error Continue” is displayed.
In this case, Try wraps the Transform Message. We may expect errors from the MULE namespace due to errors in the DataWeave engine. For example, if division by zero occurs On Error Continue will handle exception MULE:EXPRESSION. I have decided to continue flow after the error therefore, I selected the On Error Continue strategy. To achieve that, I set a default payload in the error handler. The next mule passes this payload to the next event processor in the main-continue-flow.
Summary
In my opinion, this is a major improvement. The introduced layer of abstraction on the error level was a good idea. Most noteworthy are two strategies to handle all possible error scenarios. But they can achieve all that was possible in Mule 3.x. While in the previous version, errors can be handled on the whole flow/subflow level, we can now handle errors on the event message level. Using try scope, we can achieve more granular error handling if necessary.