Exception Handling in EMA C++

This article describes the error and exception handling in EMA C++ API. It also provides sample scenarios that EMA could generate an exception. In general, the Exception can be anything which interrupts the normal flow of the program. When an exception occurs program processing gets terminated and doesn’t continue further. In such cases, we get a system generated an error message. The good thing about exceptions is that some of them can be handled and EMA also has an internal mechanism to detect the error. We believe that if the software developer understands the condition that may trigger the exception, they could avoid the case and they could have a proper solution to handle the issue. Also when the problem occurs user or application developer may need to know the reason the problem occurs so that they can fix it or reports the issue to relevance group.

EMA Exception classes

Like other TREP APIs such as RFA API; EMA also provides exception class to catch a known error. If the EMA detects the error condition, EMA will throw an exception. All exceptions in the EMA inherit from the parent class OmmException, which provides functionality and methods common across all OmmException types. The following class diagram demonstrates the EMA Exception class.

EMAExceptionclassDiagram

Based on the class diagram, EMA was designed to support the following exception types.

Exception Type Description
OmmInaccessibleLogFileException Thrown when the user running EMA application has no proper file writing permissions in a configured folder/directory. The class also provide method get Filename which returns the inaccessible file name.
OmmInvalidConfigurationException Thrown when the EMA detects an unrecoverable configuration error. The user can call get Text to shows the error message.
OmmInvalidHandleException The exception is thrown when an invalid/unrecognized item handle is passed in on OmmConsumer or OmmProvider class methods. OmmConsumer uses UInt64 values, called handles to identify individual item streams. OmmConsumer validates each passed in handle against all open and known handles. The user can call getHandle to verify the value of handle causing exception
OmmInvalidUsageException Thrown when the EMA detects invalid interface usage. Normally developer can call get Text to find the details or reason EMA generate the exception.
OmmMemoryExhaustionException Thrown when the EMA detects an out-of-memory condition. EMA internal thrown this kind of exception when malloc() returns a null pointer, operator new throws std::bad_alloc exception.
OmmOutOfRangeException Thrown when a passed-in method argument or parameter lies outside the valid range. A developer can call getText to find details about the exception.
OmmSystemException Thrown when the EMA detects a system exception. The class also provide method getSystemExceptionCode to find a code of system exception with method getSystemExceptionAddress to get an address of the system exception. However, it seems to be the class which was design for future use as it does not call by EMA internal implementation based on latest EMA source files.
OmmUnsupportedDomainTypeException Thrown if domain type specified on a message is not supported. This kind of exception is thrown when a domain type value is greater than 255.
   

EMA Exception Notification

EMA provides two ways of notifying the application about encountered user errors.

By default, EMA throws a respective exception when an error is encountered and typically, the application can use a simple try/catch blocks to catch the exception. However, a user may specify to receive a respective callback instead of an exception. 

According to the EMA C++ Developer Guide, we recommend a developer to use this approach during application development and QA to quickly detects and fix any EMA usage or application design errors. Below is sample codes from EMA consumer example which using simple try and catch blocks to catch EMA exception.

try {
        AppClient client;
         OmmConsumer consumer( OmmConsumerConfig().host( "localhost:14002" ).username( "user" ) );
         consumer.registerClient( ReqMsg().serviceName( "DIRECT_FEED" ).name( "IBM.N" ), client );
         sleep( 60000 );
} catch ( const OmmException& excp ) 
{
        cout << excp << endl;
}

Alternative ways are to uses OmmError client class. EMA Application will receive a respective callback instead of the exception. EMA provides two Error Client classes: OmmConsumerErrorClient and OmmProviderErrorClient. These two classes are an alternate error notification mechanism in the EMA, which you can use instead of the default error notification mechanism (i.e., OmmException).

To use Error Client, applications need to implement their own error client class, override the default implementation of each method, and pass this Error Client class on the constructor to OmmConsumer and OmmProvider.

Note that based EMA header files, there is some limitation of the OmmConsumerErrorClient and OmmProviderErrorClient. Both error client class does not handles callbacks for OmmInvalidConfigurationException, OmmOutOfRangeException and OmmUnsupportedDomainTypeException. The application has to implement try and catch blocks to catch an error instead.

The following sample codes illustrate an application error client and depict simple processing of the OmmConsumerErrorClient for EmaConsumer application. Note the sample codes in this article created based on EMA example 422MarketPriceErrorClient. Please download the EMA C++ package and then modify the example to test the exception.

class AppErrorClient : public thomsonreuters::ema::access::OmmConsumerErrorClient
{
public :
    void onInvalidHandle( thomsonreuters::ema::access::UInt64, const thomsonreuters::ema::access::EmaString& );
    void onInaccessibleLogFile( const thomsonreuters::ema::access::EmaString&, const thomsonreuters::ema::access::EmaString& );

    void onSystemError( thomsonreuters::ema::access::Int64, void* , const thomsonreuters::ema::access::EmaString& );
    void onMemoryExhaustion( const thomsonreuters::ema::access::EmaString& );
    void onInvalidUsage( const thomsonreuters::ema::access::EmaString& );
};

void AppErrorClient::onInvalidHandle( UInt64 handle, const EmaString& text )
{
    cout << endl << "onInvalidHandle callback function" << endl;
    cout << "Invalid handle: " << handle << endl;
    cout << "Error text: " << text << endl;
}

void AppErrorClient::onInaccessibleLogFile( const EmaString& fileName, const EmaString& text )
{
    cout << endl << "onInaccessibleLogFile callback function" << endl;
    cout << "Inaccessible file name: " << fileName <<endl;
    cout << "Error text: " << text << endl;
}

void AppErrorClient::onSystemError( Int64 code, void* address, const EmaString& text)
{
    cout << endl << "onSystemError callback function" << endl;
    cout << "System Error code: " << code << endl;
    cout << "System Error Address: " << address << endl;
    cout << "Error text: " << text << endl;
}

void AppErrorClient::onMemoryExhaustion( const EmaString& text )
{
    cout << endl << "onMemoryExhaustion callback function" << endl;
    cout << "Error text: " << text << endl;
}

void AppErrorClient::onInvalidUsage( const EmaString& text )
{
    cout << "onInvalidUsage callback function" << endl;
    cout << "Error text: " << text << endl;
}

Based on the following OmmConsumer constructor

OmmConsumer( const OmmConsumerConfig& config, OmmConsumerClient& adminClient, OmmConsumerErrorClient& errorClient, void* closure = 0 );

The application has to instantiate AppErrorClient object that receives error messages and then passes it to OmmConsumer constructor as following snippet of codes.

AppErrorClient errorClient;
OmmConsumer consumer( OmmConsumerConfig().username( "user" ).operationModel( OmmConsumerConfig::UserDispatchEnum ), errorClient );

After understanding how to notify the application about encountered user errors in EMA, the next section we will talk about sample use cases that my triggered each kind of Exception types.

EMA Exception use cases

This section provides sample use cases that could trigger EMA to generate each kind of Exception type. So that developer can avoid the use case and understand where they should verify and why they found the error.

OmmInaccessibleLogFileException

As described earlier, this type of exception thrown when the user running EMA application has no proper file writing permissions in a configured folder/directory. The easiest way to demonstrate the exception is to change EMA Logger configuration in EmaConfig.xml to use File and then set Logger Filename to a location which does not exist or has write protect.

<Logger>
    <Name value="Logger_1"/>
    <LoggerType value="LoggerType::File"/>
    <FileName value="c:\emalog\EmaLog"/>
    <LoggerSeverity value="LoggerSeverity::Success"/>
</Logger>

From the XML config, assume that we do not have a folder named emalog in drive c: therefore EMA application unable to create a file in the location and it will show the following messages based on sample codes from the previous section. onInaccessibleLogFile callback function Inaccessible file name: c:\emalog\EmaLog_12760.log Error text: Failed to open c:\emalog\EmaLog_12760.log file for writing log messages.

OmmInvalidConfigurationException

As mentioned earlier, this kind of exception type does not support in error client class, therefore, an application has to implement try and catch blocks to detect the error. This should be the codes at the beginning of the application when it initializes OmmConsumer or OmmProvider. The following codes demonstrate try and catch block to catch the exception. It just prints the exception to console output.

try {
    AppClient client;
    AppErrorClient errorClient;

    OmmConsumer consumer(OmmConsumerConfig().username("user"). operationModel(OmmConsumerConfig::UserDispatchEnum), errorClient);

    unsigned long long startTime = getCurrentTime();
    while (startTime + 60000 > getCurrentTime())
          consumer.dispatch(10);        
}
catch (const OmmInvalidConfigurationException& excp)
{
  cout << excp << endl;
}

To demonstrate the error, we just change the default Consumer name from Consumer_1 in EmaConfig.xml to Consumer_100 which does not exist in the configuration file.

<DefaultConsumer value="Consumer_100"/>

EMA internal codes will check the configuration and if it found invalid config, EMA will throw the exception. The application will catch the exception and print text message like this to the console.

Exception Type='OmmInvalidConfigurationException', Text='specified default consumer name [Consumer_100] was not found in the configured consumers'

OmmInvalidHandleException

The exception can occur when application pass invalid handles to EMA class method. It can replicate the error using the following simple codes.

AppClient client;
AppErrorClient errorClient;
UInt64 invalidHandle = 0;
OmmConsumer consumer(OmmConsumerConfig().username("user")  
            .operationModel(OmmConsumerConfig::UserDispatchEnum), errorClient);
consumer.reissue(ReqMsg(), invalidHandle);
consumer.submit(GenericMsg(), invalidHandle);
consumer.submit(PostMsg(), invalidHandle);
consumer.registerClient(ReqMsg().name("IBM.N2").serviceName("API_ELEKTRON_EDGE_TOKYO"), client);

unsigned long long startTime = getCurrentTime();
while (startTime + 60000 > getCurrentTime())
    consumer.dispatch(10);

In this example, we just pass handle which we know it’s invalid to EMA class method. General use cases are when Application calls reissue client, submit a generic message or submit a post message. It has to pass a handle to EMA methods. When the problem occurs, AppErrorClient will receive a callback for OmmInvalidHandleException and it will show the following message in the callback function. The invalid handle value we use is 0.

onInvalidHandle callback function
Invalid handle: 0
Error text: Attempt to use invalid Handle on reissue(). Instance name='Consumer_1_1'.

onInvalidHandle callback function
Invalid handle: 0
Error text: Attempt to use invalid Handle on submit( const GenericMsg& ). Instance name='Consumer_1_1'.

onInvalidHandle callback function
Invalid handle: 0
Error text: Attempt to use invalid Handle on submit( const PostMsg& ). Instance name='Consumer_1_1'.

This kind of error relate with a value of Handle, therefore, we would suggest applying to add information about handles return from EMA and handles it pass to EMA class method to application's log. Some item handle might be invalid in case that it’s invalid RIC or no permission. An application has to manage the EMA Handles properly.

OmmInvalidUsageException

This kind of exception thrown when the EMA detects invalid interface usage and application has to implement try and catch blocks to detect the error. EMA internal can throw this exception in many cases and we have to check information from the getText method for the reason it throws an exception. One use case that often sees by new EMA developer is the case that the data contains blank value but the application does not call fieldEntry.getCode() to verify if it’s Blank data before they get value from the FieldEntry. EMA will throw the invalid usage exception in this case. There are some scenarios that EMA internal layer may receive unexpected data or message from infrastructure and it could throw OmmInvalidUsageException as well. This scenario can be incident on infrastructure upside. Thus we would suggest developer add to try and catch blocks around the codes doing OMM data processing to catch the exception.

OmmOutOfRangeException

As described earlier, EMA throws the exception when a passed-in method argument lies outside the valid range. Below is sample use case when application access data inside EmaString object.

try {    
    EmaString str = "TRI.N";
    cout << str.substr(0, 6) << endl;        
}
catch (const OmmOutOfRangeException& excp)
{
    cout << excp << endl;
}

The example uses EmaString to keep string “TRI.N” which contains 5 characters and then application calls EmaString::substr method to shows substring but accidentally pass an invalid length of the substring to the method. In this EMA can detect and it will throw OmmOutOfRangeException and print the following message.

Exception Type='OmmOutOfRangeException', Text='Attempt to access out of range position in EmaString::substr( UInt32 , UInt32 ) const. Passed in index is 0 and passed in length is 6 while length is 5.'

This issue can occur when the application tries to access a character in a string using codes like str[6] but the index it passes out of range. EMA can detect the error and throw this kind of exception as well.

OmmUnsupportedDomainTypeException

It can replicate this kind of exception when EMA application set ReqMsg with domainType more than 255. The following sample codes can generate the exception print error message in try and catch blocks.

try {
    AppClient client;
    AppErrorClient errorClient;
    OmmConsumer consumer(OmmConsumerConfig().username("user")
                   .operationModel(OmmConsumerConfig::UserDispatchEnum), errorClient);

    consumer.registerClient(ReqMsg().name("IBM.N")
              .serviceName("API_ELEKTRON_EDGE_TOKYO").domainType(257), client);

    unsigned long long startTime = getCurrentTime();
    while (startTime + 60000 > getCurrentTime())
        consumer.dispatch(10);        
}
catch (const OmmUnsupportedDomainTypeException& excp)
{
    cout << excp << endl;
}

It set domainType to 257 which is out of range value. EMA will throw an exception with the following error message.

Exception Type='OmmUnsupportedDomainTypeException', Text='Passed in DomainType is out of range.', DomainType='257'

To avoid the exception, the application should verify the range of domain type before passing to the method.

OmmMemoryExhaustionException

As described earlier, EMA internal thrown the exception when malloc() returns a null pointer, operator new throws std::bad_alloc exception. The situation or scenario that may cause this kind of exception could be the case that application has memory leak or growth until it reaches OS limit. Some application may need to cache data in memory or having their own queue. Therefore it requires more memory and it could cause out of memory error. If EMA call an internal method to allocate new memory but it’s failed, it will throw the exception.

Conclusion

Exception or error can occur in any application. When it occurred; application getting terminated and doesn’t continue further. EMA provides exception handling class to detect some specific error condition. All exceptions in EMA inherit from parent class OmmException. The exception handling can prevent a program from getting terminated and some of the error can be recovering by proper application logic. Though exception handling codes may reduce some performance of Real-Time application we believe that it's nice to have exception handling in the application as this could be avoided some critical issue.

References

  • EMA example 422MarketPriceErrorClient from EMA C++ Package. Locates in EMA Installation Folder\Ema\Examples\Training\Consumer\