Errors and exception handling

Correct handling of errors and exceptions is important for correct integration node operation. You must consider how and when your user-defined extension must handle errors and exceptions.

The errors and exception handling here describes factors that you must consider when you develop user-defined extensions for IBM® Integration Bus in the C programming language. If you are developing user-defined extensions using the Java™ programming language, you can use standard Java error and exception handling methods. If, for example, IBM Integration Bus throws an exception internally, a Java exception of class MbException is made available.

The integration node generates C++ exceptions to handle error conditions. These exceptions are caught in the relevant software layers in the integration node, and are handled accordingly. However, programs written in C cannot catch C++ exceptions, and all exceptions thrown, by default, bypass all C user-defined extension code and are caught in a higher layer of the integration node.

Utility functions, by convention, typically use the return value to pass back requested data; for example, the address or handle of an integration node object. The return value sometimes indicates that a failure has occurred. For example, if the address or handle of an integration node object could not be retrieved, zero (CCI_NULL_ADDR) is returned. Additionally, the reason for an error condition is stored in the return code output parameter, which is, by convention, part of the function prototype of all utility functions. If the utility function completed successfully and returnCode was not null, returnCode contains CCI_SUCCESS. Otherwise, it contains one of the return codes described here. You can test the value of returnCode to determine whether a utility function was successful.

If the call to a utility function causes the integration node to generate an exception, the error is visible to the user-defined extension only if it specified a value for the returnCode parameter to that utility function. If a null value was specified for returnCode, and an exception occurs:
  • The user-defined extension is not be aware of that exception
  • The utility function does not return to the user-defined extension
  • Execution control passes to higher layers in the integration node stack to process the exception

Therefore, a user-defined extension cannot perform its own error recovery. If, however, the returnCode parameter is specified, and an exception occurs, a return code of CCI_EXCEPTION is returned. In this case, cciGetLastExceptionData or cciGetLastExceptionDataW (the difference being that cciGetLastExceptionDataW returns a CCI_EXCEPTION_WIDE_ST which can contain Unicode trace text) can be used to obtain diagnostic information on the type of exception that occurred. The data is returned in the CCI_EXCEPTION_ST or CCI_EXCEPTION_WIDE_ST structure.

If there are no resources to be released, do not set the returnCode argument in your user-defined extension. Not setting this argument allows exceptions to bypass your user-defined extensions. These exceptions can then be handled higher up the IBM Integration Bus stack, by the integration node.

Message inserts can be returned in the CCI_STRING_ST members of the CCI_EXCEPTION_ST structure. The CCI_STRING_ST allows the user-defined extension to provide a buffer to receive all required inserts. The integration node copies the data into this buffer, and returns the number of bytes output and the actual length of the data. If the buffer is not large enough, no data is copied and the "dataLength" member can be used to increase the size of the buffer, if required.

The user-defined extension can set a non-null value for returnCode and provide its own error recovery, if required. The utility function calls return to the user-defined extension and pass their status through returnCode. All exceptions that occur in a utility function must be passed back to the integration node for additional error recovery to be performed; that is, when CCI_EXCEPTION is returned in returnCode. You do this by calling cciRethrowLastException, after the user-defined extension has completed its own error processing. Calling cciRethrowLastException causes the C interface to re-throw the last exception so that it can be handled by other layers in the integration node. In the same way as the C exit call, cciRethrowLastException does not return in this case.

If an exception occurs and is caught by a user-defined extension, the extension must not call utility functions except cciGetLastExceptionData, cciGetLastExceptionDataW, or cciRethrowLastException. An attempt to call other utility functions results in unpredictable behavior that can compromise the integrity of the integration node.

If a user-defined extension encounters a serious error, cciThrowException or cciThrowExceptionW can be used to generate an exception that is processed by the integration node in the correct manner. The generation of such an exception causes the supplied information to be written to the system log (syslog or Eventviewer) if the exception is not handled. The information is also written to trace (if trace is active).

Types of exception and integration node behavior

The integration node generates a set of exceptions that can be passed to a user-defined extension. These exceptions can also be generated by a user-defined extension when an error condition is encountered. The exception classes are:

Fatal
Fatal exceptions are generated when a condition occurs that prevents the integration node process from continuing execution safely, or where it is integration node policy to terminate the process. Examples of fatal exceptions are a failure to acquire a critical system resource, or an internally-caught severe software error. The integration node process terminates following the throwing of a fatal exception. Fatal exceptions are logged.
Recoverable
These exceptions are generated for errors which, although not terminal in nature, mean that the processing of the current message flow has to be ended. Examples of recoverable exceptions are invalid data in the content of a message, or a failure to write a message to an output node. When a recoverable exception is thrown, the processing of the current message is canceled on that thread, but the thread recommences execution at its input node. Recoverable exceptions are not logged.
Configuration
Configuration exceptions are generated when a configuration request fails. This can be because of an error in the format of the configuration request, or an error in the data. When a configuration exception is thrown, the request is rejected and an error response message is returned. Configuration exceptions are not logged.
Parser
These exceptions are generated by message parsers for errors that prevent the parsing of the message content or creating a bit stream. A parser exception is treated as a recoverable exception by the integration node. Parser exceptions are normally logged unless they are considered as recoverable exceptions.
Conversion
These exceptions are generated by the integration node character conversion functions if invalid data is found when trying to convert to another data type. A conversion exception is treated as a recoverable exception by the integration node and is therefore not logged.
User
These exceptions are generated when a Throw node throws a user-defined exception. User exceptions are logged.
Database
These exceptions are generated when a database management system reports an error during integration node operation. A database exception is treated as a recoverable exception by the integration node. Database exceptions are logged if they result in a fatal condition.