MuleSoft error handlers are powerful tools to ensure that your programs can recover gracefully from unexpected events. This post explains these tools specific to Java error handling, to help Java developers quickly grasp MuleSoft error-handling concepts.
While MuleSoft’s powerful error-handling tools can be difficult to understand at first, the concepts behind them are similar to those of other object-orientated programming languages. In this post, we’ll look at the individual elements of MuleSoft’s error-handling suite. For each one, we’ll describe its functionality in terms of this example Java code:
In this contrived example, the Java code accepts two Strings and attempts to convert them into integers. If this conversion fails, the code throws a NumberFormatException, which must be dealt with higher up the call stack. Otherwise, integer division is attempted and the result is returned. In the case of division by zero, the code catches an ArithmeticException, and returns “infinity,” as if the operation had completed successfully.
We will look at the ‘throws’ case and the ‘catch’ case separately, and build Mule code to handle each case. At the end, we will see how we can combine both cases together.
The On-error-continue handler works similarly to the Java catch statement. This is used when dealing with the problem where it occurs, and allows normal execution to continue.
In the example above, we catch an ArithmeticException, which occurs at line 16. Execution then skips the logger at line 18, and jumps to line 24, where we set the result to be “infinity” and return it, as if no error occurred. Whatever called this method simply gets the value of Integer.MAX_VALUE returned, with no indication that an exception was handled.
Here is how this portion of the implementation might look within Mule:
In both Java and Mule, the log message is skipped if the division fails. Regardless of whether the integer division was successful, an integer value is returned, and the caller believes the flow executed successfully.
To return the static Java field Integer.MAX_VALUE, we can use Mule’s Java module directly within our Set Payload transformer:
Note that instead of a period . we use a double colon :: to delimit packages in Mule.
Some Anypoint Studio versions may flag the above with an “Unable to resolve reference” validation error. This is a known issue with the validation module and can be ignored in this case.
The On-error-propagate handler works similarly to the Java throws statement. This is used when we need the caller to know that a problem occurred, if for instance we are unable to deal with the problem at its source.
In the Java example above, if either x or y cannot be parsed into an Integer value, we throw a NumberFormatException at either line 13 or line 14. This exception is then propagated up the call stack, where it then needs to be handled by whatever called the integerDivision() method.
Here is how the implementation might look like in Mule:
In this case, if any problem occurs, the error is simply thrown back up the stack, and the Transform Message and Logger processors do not execute. Because we don’t want to perform any action in this case, the on-error-propagate scope is left empty.
We could also specify actions to take before throwing/propagating the error, by adding them to the on-error-propagate scope. In Java, this would require an extra try/catch block.
Let’s combine the two flows to give a representation of a Mule implementation of the example Java code.
Our immediate problem is, if we have multiple error handlers (here on-error-continue and on-error-propagate), we need to specify error namespaces for them to listen to. The last error handler (here on-error-propagate) will catch all other errors if it does not define an error namespace.
The reason this is a problem is that in Mule, both a failed conversion from a String to a Number and a division by zero would raise a MULE:EXPRESSION error. This is because both problems occur within a DataWeave expression.
To handle the two errors differently, we have to create a custom Error type within Mule, raise that error within a Try scope, and then propagate it up so that the caller (the Flow) has to handle it.
Once we’ve made those changes, the Mule implementation might look like this:
Here, we raise an error with a custom type of APP:ARITHMETIC_EXCEPTION if we attempt to divide by zero. We then propagate the error up. This means it needs to be handled by whatever called the Try scope — which in this case is simply the flow integerDivider. The flow-level error handling then takes over, and the on-error-continue handler catches the APP:ARITHMETIC_EXCEPTION error, sets a default payload, and returns it as if the flow had completed successfully.
If the incoming variables can’t be mapped into the Number type, that will raise a MULE:EXPRESSION error, which is then handled and thrown by the on-error-propagate handler. In this case there are no actions to perform, so the handler simply throws the error up the stack. It then needs to be handled by whatever originally called the integerDivider flow.
Here is the Mule XML code for the integerDivider flow:
<flow name="integerDivider"> <set-variable value="#[vars.x as Number]" doc:name="intx = vars.x as Number" variableName="intx"/> <set-variable value="#[vars.y as Number]" doc:name="inty = vars.y as Number" variableName="inty"/> <try doc:name="Try"> <ee:transform doc:name="divide-intx-by-inty"> <ee:message> <ee:set-payload><![CDATA[%dw 2.0 output application/dw --- vars.intx / vars.inty]]></ee:set-payload> </ee:message> </ee:transform> <error-handler > <on-error-propagate enableNotifications="false" logException="false" doc:name="On Error Propagate"> <raise-error doc:name="APP:ARITHMETIC_EXCEPTION" type="APP:ARITHMETIC_EXCEPTION" description="APP:ARITHMETIC_EXCEPTION" /> </on-error-propagate> </error-handler> </try> <logger level="INFO" doc:name='"Successfully performed integer division."' message="Successfully performed integer division."/> <error-handler > <on-error-continue enableNotifications="false" logException="false" doc:name="On Error Continue" type="APP:ARITHMETIC_EXCEPTION"> <set-payload value="#[java!java::lang::Integer::MAX_VALUE]" mimeType="application/dw" doc:name="java!java::lang::Integer::MAX_VALUE"/> <logger level="INFO" doc:name="Logger" message="#[payload]"/> </on-error-continue> <on-error-propagate enableNotifications="false" logException="false" doc:name="On Error Propagate" type="MULE:EXPRESSION"/> </error-handler> </flow>
In Java we can create our own custom exceptions, and Mule allows us to do the same. The difference is that in Mule, we can simply declare a custom error type inline — like APP:ARITHMETIC_EXCEPTION above — without having to declare and implement a new Exception class. All we need to do is declare the custom error type within a Raise Error component.
In Mule we can map an existing error type to a custom one. This would be done in the Mule component which raises the error.
System and messaging errors
System errors are caused by problems at the Mule-system level, for instance problems with the Mule Runtime itself or failed connections to an external system. System errors are like instantiations of the Java java.lang.Error class: they indicate “serious problems that a reasonable application should not try to catch.” In MuleSoft, you cannot specifically catch or handle a System Error.
Messaging errors on the other hand, indicate problems which the application should try to recover from or handle. These are like instantiations of the Java java.lang.Exception class. Messaging errors can be caught and handled similarly to Java Exceptions.
In this post we’ve shown how to think of the MuleSoft error-handling components in terms of Java exception handling. For further posts regarding error handling in Mule, you can search for the tag “error handling,” or click on this link.
Further in this series we will look at other common connectors and event processors in Mule, and compare and contrast them with common Java terminologies. For more error-handling tactics, checkout this blog series on demystifying error handling in Mule.