Polling for events

For a C++ connector, the GenGlobals class defines the pollForEvents() method. You must provide an implementation of this virtual method as part of your connector class.

Note:
For an introduction to event notification, see "Event notification".. For a discussion of event-notification mechanisms and the implementation of pollForEvents(), see Event notification.

The C++-based pseudo-code in Figure 71 shows the basic logic flow for a pollForEvents() method. This method first retrieves a pointer to the subscription manager. The subscription manager manages subscriptions to business objects supported by the connector.

The pollForEvents() method then retrieves a set of events from the event store and, for each event, the method calls the subscription manager class method isSubscribed() to determine whether any subscriptions exist for the corresponding business object. If there are subscriptions, the method retrieves the data from the application, creates a new business object, and calls the subscription manager method gotApplEvent() to send the business object to InterChange Server. If there are no subscriptions, the method archives the event record with a status value of unprocessed.

Figure 71. C++ pollForEvents() example

int ExampleGenGlob::pollForEvents() 
 { 
    SubscriptionHandlerCPP *mySubHandler =
       GenGlobals::getTheSubHandler();         
    int status = 0; 
    get the events from the event list
    for events 0 to PollQuantity in eventlist {
       extract BOName, verb, and key from the event record
       if(mySubHandler->isSubscribed(BOName,BOverb) {
          BO = new BusinessObject(BOName) 
          BO.setAttrValue(key) 
          retrieve application data using doVerbFor()
          BO.setVerb(Retrieve) 
          BO.doVerbFor() 
          BO.setVerb(BOverb) 
          status = mySubHandler->gotApplEvent(BusinessObject);
          archive event record with success or failure status 
       } 
       else {
          archive event record with unprocessed status      }
    return status;
 }
 
Note:
For a flow chart of the poll method's basic logic, see Figure 52..

This section provides more detailed information on each of the steps in the basic logic for the event processing that the pollForEvents() method typically performs. Table 78 summarizes these basic steps.

Table 78. Basic logic of the pollForEvents() method

Step For more information
1. Set up a subscription manager for the connector. "Accessing the subscription manager"
2. Verify that the connector still has a valid connection to the event store. Verifying the connection before accessing the event store
3. Retrieve specified number of event records from the event store and store them in an events array. Cycle through the events array. For each event, mark the event in the event store as In-Progress and begin processing. "Retrieving event records"
4. Get the business object name, verb, and key data from the event record. "Getting the business object name, verb, and key"
5. Check for subscriptions to the event. "Checking for subscriptions to the event"

If the event has subscribers:

  • Retrieve application data and create the business object.

"Retrieving application data"

  • Send the business object to the connector framework for event delivery.

"Sending the business object to the connector framework"

  • Complete event processing.

"Completing the processing of an event"

If the event does not have subscribers, update the event status to Unsubscribed. "Checking for subscriptions to the event"
6. Archive the event. "Archiving the event"

Accessing the subscription manager

As part of connector initialization, the connector framework instantiates a subscription manager. This subscription manager keeps the subscription list current. (For more information, see "Business object subscription and publishing".) A C++ connector has access to the subscription manager and the connector subscription list through a subscription handler, which is encapsulated by the SubscriptionHandlerCPP class. It can use methods of this class to determine whether business objects have subscribers and to send business objects to the connector controller.

Table 79 lists the method that the C++ connector library provides to obtain a reference to a subscription handler.

Table 79. Method for obtaining a subscription handler

C++ connector library class Method
GenGlobals getTheSubHandler()

For a C++ connector, the pollForEvents() method first sets up a subscription manager for the connector by calling the GenGlobals::getTheSubHandler(). For example:

SubscriptionHandlerCPP *mySub = GenGlobals::getTheSubHandler();
 

The getTheSubHandler() method returns a pointer to the subscription manager, which is an instance of the SubscriptionHandlerCPP class. Through this subscription handler, the connector can query its subscription manager to find out whether the integration broker is interested in a particular type of business object.

Verifying the connection before accessing the event store

When the init() method in the connector class initializes the application-specific component, one of its most common tasks is to establish a connection to the application. The poll method requires access to the event store. Therefore, before the pollForEvents() method begins processing events, it should verify that the connector is still connected to the application. The way to perform this verification is application-specific. Consult your application documentation for more information.

A good design practice is to code the connector application-specific component so that it shuts down whenever the connection to the application is lost. If the connection has been lost, the connector should not continue with event polling. Instead, it should take the steps in Table 56 to notify the connector framework of the lost connection. The pollForEvents() method should return BON_APPRESPONSETIMEOUT to notify the connector framework of the loss of connection to the application.

Figure 72 contains code to handle the loss of connection to the application. In this example, error message 20018 is issued to inform an administrator that the connection from the connector to the application has been lost and that action needs to be taken.

Figure 72. Loss of connection in pollForEvents()

int ExampleGlobals::pollForEvents() 
 {
    ...
  
    if (//application is not responding ) {
       // Lost connection to the application
       // Log an error message
       logMsg(generateMsg(20018, CxMsgFormat::XRD_FATAL, NULL, 0,
                      "MyConnector"));
  
       // Populate a ReturnStatusDescriptor object
       char errorMsg[512];
       sprintf(errorMsg, "Lost connection to application");
       rtnObj->seterrMsg(errorMsg);
  
       return BON_APPRESPONSETIMEOUT;      }
  
       ....
       // if connection is open, continue processing
       ...
 } 
 

Retrieving event records

To send event notifications to the connector framework, the poll method must first retrieve event records from the event store. For a C++ connector, you must use an application-specific interface to retrieve event records from the event store.

The poll method can retrieve one event record at a time and process it or it can retrieve a specified number of event records per poll and cache them to an events array. Processing multiple events per poll can improve performance when the application generates large numbers of events.

The number of events picked up in any polling cycle should be configurable using the connector configuration property PollQuantity. At install time, a system administrator sets the value of PollQuantity to an appropriate number, such as 50. The poll method can use the getConfigProp() to retrieve the value of the PollQuantity property, and then retrieve the specified number of event records and process them in a single poll.

Note:
Because many C++ connectors are single-threaded, the connector framework does not accept request business objects while the poll method is running. This means that request processing is blocked while the poll method is processing events. Keep this in mind when implementing the processing of multiple events per poll. For more information on single- and multi-threaded C++ connectors, see "Threading issues"..

The connector should assign the In-Progress status to any event that it has read out of the event store and has started to process. If the connector terminates while processing an event and before updating the event status to indicate that the event was either sent or failed, it will leave an In-Progress event in the event store. For more information on how recover these In-Progress events, see "Recovering In-Progress events"..

To retrieve event records from the event store, a C++ connector must use whatever technique the application provides. As an example, an implementation for a table-based application might create an event table from which the connector application-specific code retrieves event data. The code fragment in Figure 73 shows how a connector for this kind of implementation can retrieve events from an event table using the ODBC API and ODBC SQL commands. The program initially defines the macro for a SQL SELECT statement that retrieves event records from the event table.

#define GET_EVENT_QUERY                                   \ 
       "SELECT event_id, object_name, object_verb, object_key \
       FROM cw_events WHERE event_status = 0 \
       ORDER BY event_time"
 

This SELECT statement retrieves the event identifier, business object name, verb, and key data from those event records whose status is 0 ( Ready-for-Poll). These event records are ordered by their timestamp. For information on retrieving event records by event priority, see "Processing events by event priority"..

The poll method allocates memory for the data, binds the memory to specific columns of data, and retrieves the data one event record at a time. For the complete example program, see Example of a basic pollForEvents() method..

Figure 73. Retrieving an event

// Allocate a statement handle for the SQL statement 
 rc = SQLAllocStmt(gHdbc1, &hstmt1);
  
 // Execute the SELECT query. Use the macro GET_EVENT_QUERY
 rc = SQLExecDirect(hstmt1, GET_EVENT_QUERY, SQL_NTS);
  
 // Allocate memory for event data
 ev_id = new char[80];
 obj_name = new char[80];
 obj_verb = new char[80];
 obj_key = new char[80];
  
 query = new char[255];
 key_value = new char[80];
  
 // Bind all results set columns to event variables
 rc = SQLBindCol(hstmt1, 1, SQL_C_CHAR, ev_id,    80, &cbValue); 
 rc = SQLBindCol(hstmt1, 2, SQL_C_CHAR, obj_name, 80, &cbValue);
 rc = SQLBindCol(hstmt1, 3, SQL_C_CHAR, obj_verb, 80, &cbValue);
 rc = SQLBindCol(hstmt1, 4, SQL_C_CHAR, obj_key,  80, &cbValue);
  
 // Fetch the event data
 while (rc == SQL_SUCCESS || rc != SQL_SUCCESS_WITH_INFO) {
       ev_id = '\0';
       obj_name = '\0';
       obj_verb = '\0';
       obj_key = '\0';
  
       rc = SQLFetch(hstmt1);
       if (rc == SQL_SUCCESS) {
          // Process the record
          if ((cp = strchr(ev_id, ' ')) != NULL)
             *cp = NULL;
          if ((cp = strchr(obj_name, ' ')) != NULL)
             *cp = NULL;
          if ((cp = strchr(obj_verb, ' ')) != NULL)
             *cp = NULL;
          if ((cp = strchr(obj_key, ' ')) != NULL)
             *cp = NULL;
       }
 
Note:
This example uses the standard ODBC API to retrieve event data from the event table. If your application has an API that provides access to data in the event table, use the application API.

Getting the business object name, verb, and key

Once the connector has retrieved an event, it extracts the event ID, the object key, and the name and verb of the business object from the event record. The connector uses the business object name and verb to determine whether the integration broker is interested in this type of business object. If the business object and its active verb have subscribers, the connector uses the entity key to retrieve the complete set of data.

For a C++ connector, you must use an application-specific interface to obtain this information from the event records in the event store. Figure 73 showed one way that a C++ connector can process event data. In the example, the appropriate event data is retrieved into the connector variables:

Event ID ev_id
Business object name obj_name
Verb obj_verb
Object key obj_key

The connector should send the business object with the same verb that was in the event record.

Checking for subscriptions to the event

To determine whether the integration broker is interested in receiving a particular business object and verb, the poll method calls the isSubscribed() method. The isSubscribed() method takes the name of the current business object and a verb as arguments. The name of the business object and verb must match the name of the business object and verb in the repository.

WebSphere InterChange Server

If your business integration system uses InterChange Server, the poll method can determine if any collaboration subscribes to the business object with a particular verb. At initialization, the connector framework requests its subscription list from the connector controller at connector initialization. At runtime, the application-specific component can use isSubscribed() to query the connector framework to verify that some collaboration subscribes to a particular business object. The application-specific connector component can send the event only if some collaboration is currently subscribed.

Other integration brokers

If your business integration system uses a WebSphere message broker (WebSphere MQ Integrator, WebSphere MQ Integrator Broker, or WebSphere Business Integration Message Broker) or WebSphere Application Server, the connector framework assumes that the integration broker is interested in all the connector's supported business objects. If the poll method uses the isSubscribed() method to query the connector framework about subscriptions for a particular business object, the method returns true for every business object that the connector supports.

Table 80 lists the method that the C++ connector library provides to check for subscriptions to the event.

Table 80. Method for checking subscriptions

C++ connector library class Method
SubscriptionHandlerCPP isSubscribed()

Based on the value that isSubscribed() returns, the poll method should take one of the following actions:

No other processing should be done with unsubscribed events. If at a later date, the integration broker subscribes to these events, a system administrator can move the unsubscribed event records from the archive store back to the event store.

As Table 80 shows, the isSubscribed() method is provided in the subscription manager, the SubscriptionHandlerCPP object. The method returns 1 if there are subscribers, and 0 if there are no subscribers. An example of a C++ connector checking for subscriptions follows:

if (mySubHndlr->isSubscribed(obj_name, obj_verb) = TRUE) {
       // handle event
    }
 else {
       // archive the event (if archiving is supported)
    }
 

Retrieving application data

If there are subscribers for an event, the poll method must take the following steps:

  1. Retrieve the complete set of data for the entity from the application.

    To retrieve the complete set of entity data, the poll method must use name of the entity's key information (which is stored in the event) to locate the entity in the application database. The poll method must retrieve the complete set of application data when the event has the following verbs:

    For a Delete event from an application that supports physical deletes, the application may have already deleted the entity from the database, and the connector may not be able to retrieve the entity data. For information on delete processing, see "Processing Delete events"..

  2. Package the entity data in a business object.

    Once the populated business object exists, the poll method can publish the business object to subscribers.

Table 80 lists the methods that the C++ connector library provides to retrieve entity data from the application database and populate a business object.

Table 81. Methods for retrieving business object data

C++ Connector Library Class Method
BusinessObject doVerbFor(),, getAttrCount(),, getAttrType(),, getAttrValue(),, setAttrValue(),, setVerb()
Note:
If the event is a delete operation and the application supports physical deletions of data, the data has most likely been deleted from the application, and the connector cannot retrieve the data. In this case, the connector simply creates a business object, sets the key from the object key of the event record, and sends the business object. For more information on handling delete events, see "Processing Delete events"..

For a C++ connector, the standard way of retrieving application data from within pollForEvents() is to use the Retrieve method in the connector's business object handler. With this approach, you do not have to recode data retrieval in the pollForEvents() method.

To retrieve application data using the doVerbFor() method of the BusinessObject class, follow these general steps:

  1. Create a new business object instance for the business object that corresponds to the application entity.
  2. Call BusinessObject::setVerb() to set the verb of the business object to Retrieve.
  3. Get the key or keys for the application entity from the event record.
  4. Call BusinessObject::setAttrValue() to set the key values in the business object.

    The setAttrValue() method takes as arguments the name of the attribute, a string representation of the value or pointer to the value of the attribute, and the attribute type.

  5. Call BusinessObject::doVerbFor() on this business object.

    The BusinessObject class implementation of the doVerbFor() method calls the business object handler specified in the business object definition to perform the action of the verb. The doVerbFor() method operates on the current business object, filling the business object with the current values of the application entity.

The C++ code fragment in Figure 74 illustrates this approach.

Figure 74. Retrieving application data

// Create a new business object
 pBusObj = new BusinessObject(obj_name);
  
 // Set verb to Retrieve
 pBusObj->setVerb("Retrieve");
  
 // Extract value of key from key:value pair 
 if ((cp = strchr(obj_key, ':')) != NULL) {
    cp++;
    strcpy(key_value, cp);
    cp--;
    *cp = NULL;
 }
  
 // Find the key attribute in the business object and 
 // set it to the key value
 for (i = 0; i < pObj->getAttrCount()-1; i++) {
    if (pBusObj->getSpecFor()->getAttribute(i)->isKey()) {
       pBusObj->setAttrValue(pBusObj->getAttrName(i),key_value, 
          pBusObj->getAttrType(pBusObj->getAttrName(i)));
    }
 }
  
 // Call the business object handler doVerbFor() 
 if (pBusObj->doVerbFor() == BON_FAIL) {
    // Log error message if retrieve fails
    retcode = BON_FAIL;
 }
 

The code fragment in Figure 74 uses getAttrName() to get the name of each attribute that is a key. It then calls getAttrType() to get the type of the key attribute. The setAttrValue() method verifies that the new value has the correct data type before changing the attribute value.

The ObjectEventId attribute is used in the IBM WebSphere business integration system to track the flow of business objects through the system. In addition, it is used to keep track of child business objects across requests and responses, as child business objects in a hierarchical business object request might be reordered in a response business object.

Connectors are not required to populate ObjectEventId attributes for either a parent business object or its children. If business objects do not have values for ObjectEventId attributes, the IBM WebSphere business integration system generates values for them. However, if a connector populates child ObjectEventIds, the values must be unique across all other ObjectEventId values for that particular business object regardless of level of hierarchy. ObjectEventId values can be generated as part of the event notification mechanism. For suggestions on how to generate ObjectEventId values, see "Event identifier"..

Sending the business object to the connector framework

Once the data for the business object has been retrieved, the poll method performs the following tasks:

Table 80 lists the methods that the C++ connector library provides to set the business object verb and send the business object.

Table 82. Classes and methods for setting verb and sending business object

C++ connector library class Method
BusinessObject setVerb()
SubscriptionHandlerCPP gotApplEvent()

Setting the business object verb

To set the verb in a business object to the verb specified in the event record, the poll method calls the business object method setVerb(). The poll method should set the verb to the same verb that was in the event record in the event store.

Note:
If the event is a physical delete, use the object keys from the event record to set the keys in the business object, and set the verb to Delete.

For a C++ connector , the populated BusinessObject object that the doVerbFor() method returns still has a verb of Retrieve. The poll method must set the business object's verb to its original value with the setVerb() method, as the following code fragment shows:

// Set verb to action as indicated in the event record
 pBusObj->setVerb(obj_verb);
 

In this code fragment, obj_verb is the verb value that has previously been obtained from the event record, as shown in Figure 73..

Sending the business object

The poll method uses the method gotApplEvent() to send the business object to the connector framework. This method takes the following steps:

The connector framework does some processing on the event object to serialize the data and ensure that it is persisted properly. It then makes sure the event is sent.

WebSphere InterChange Server

If your business integration system uses InterChange Server, the connector framework makes sure the event is either sent to the ICS through CORBA IIOP or written to a queue (if you are using queues for event notification). If sending the event to ICS, the connector framework forwards the business object to the connector controller, which in turn performs any mapping required to transform the application-specific business object to a generic business object. The connector controller can then send the generic business object to the appropriate collaboration.

Other integration brokers

If your business integration system uses a WebSphere message broker (WebSphere MQ Integrator, WebSphere MQ Integrator Broker, or WebSphere Business Integration Message Broker) or WebSphere Application Server, the connector framework makes sure the event is converted to an WebSphere MQ message and written to the appropriate MQ queue.

The poll method should check the return code from gotApplEvent() to ensure that returned errors are handled appropriately. For example, until the event delivery is successful, the poll method should not remove the event from the event table. Table 83 shows the possible event-status values, based on whether event delivery is successful.

Table 83. Possible event status after event delivery

State of Event Delivery Event Status
If the event delivery is successful 1 (see Table 40)
If the event delivery fails -2 (see Table 40)

The following code fragment shows the call to gotApplEvent() for a C++ connector:

// Send business object to connector framework
 if (( retcode = mySub->gotApplEvent(*pBusObj)) == BON_FAIL) {
    // Log an error message 
    // Update event status to "error posting event"
    // Event remains in event table and is not archived
 }
  
 // Update event status to "event successfully posted"
 

The gotApplEvent() method returns BON_SUCCESS if the connector framework successfully delivers the business object, and returns a nonzero value (such as BON_FAIL) if the delivery fails.

Note:
If this call is not successful, the event must be archived.

Completing the processing of an event

The processing of an event is complete when the tasks in Table 84 complete.

Table 84. Steps in processing an event

Processing task For more information
The poll method has retrieved the application data for the event and created a business object that represents the event. "Retrieving application data"
The poll method has sent the business object to the connector framework. "Sending the business object to the connector framework"
Note:
For hierarchical business objects, the event processing is complete when the poll method has retrieved the application data for the parent business object and all child business objects and sent the complete hierarchical business object to the connector framework. The event notification mechanism must retrieve and send the entire hierarchical business object, not just the parent business object.

The poll method must ensure that the event status correctly reflects the completion of the event processing. Therefore, it must handle both of the following conditions:

Handling successful event processing

The processing of an event is successful when the tasks in Table 84 successfully complete. The following steps show how the poll method should finish processing a successful event:

  1. Receive a "success" return code from the gotApplEvent() method signifying the connector framework's successful delivery of the business object to the messaging system.
  2. Copy the event to the archive store. For more information, see "Archiving the event".
  3. Set the status of the event in the archive store.
  4. Delete the event record from the event store.

    Until the event delivery is successful, the poll method should not remove the event from the event table.

Note:
The order of the steps might be different for different implementations.

Handling unsuccessful event processing

If an error occurs in processing an event, the connector should update the event status to indicate that an error has occurred. Table 85 shows the possible event-status values, based on errors that can occur during event processing.

Table 85. Possible event status after unsuccessful event processing

State of Event Delivery Event Status
If an error occurs in processing an event -1 (see Table 40)
If the event delivery fails -2 (see Table 40)

For example, if there are no application entities matching the entity key, the event status should be updated to "error processing event". As discussed in "Sending the business object", the poll method should check the return code from gotApplEvent() to ensure that any errors that are returned are handled appropriately. If the event cannot be successfully delivered, its event status should be updated to "error posting event".

In either case, the event should be left in the event store to be analyzed by a system administrator. When the poll method queries for events, it should exclude events with the error status so that these events are not picked up. Once an event's error condition has been resolved, the system administrator can manually reset the event status so that the event is picked up by the connector on the next poll.

Archiving the event

Archiving an event consists of creating an archive record by moving the event record from the event store to an archive store. To archive event records to the archive store, a C++ connector must use whatever technique the application provides. Usually, the connector uses whatever method it used to access event records in the event store, such as the ODBC API and ODBC SQL commands. Consult your application documentation for more information.

Note:
For a general introduction to archiving, see "Archiving events"..

To archive event records from this event store, the poll method takes the following actions:

  1. Ensure that archiving is implemented by checking the value of the appropriate connector configuration property, such as ArchiveProcessed. For more information, see "Configuring a connector for archiving"..
  2. Copy the event record from the archive store to the event store.
  3. Update the event status of the archive record to reflect the reason for archiving the event.

    Table 86 shows the event-status values that the archive record will usually have.

    Table 86. Event-status values in an archive record

    Event-status value Description
    1 The event was detected, and the connector created a business object for the event and sent the business object to the connector framework. For more information, see "Handling successful event processing".
    2 The event was detected, but there were no subscriptions for the event, so the event was not sent to the connector framework and on to the integration broker. For more information, see "Checking for subscriptions to the event".
    -1 The event was detected, but the connector encountered an error when trying to process the event. The error occurred either in the process of building a business object for the event or in sending the business object to connector framework. For more information, see "Handling unsuccessful event processing".
  4. Delete the event record from the event store.

After archiving is complete, your poll method should set the appropriate return code:

Example of a basic pollForEvents() method

The section provides implementations of the basic logic for the pollForEvents() method in a C++ connector. The code sample in Figure 75 demonstrates event handling in a C++ connector that uses the ODBC API to communicate with an application database. In this example, the connector retrieves one event at a time and processes it.

Note:
This code assumes that the connector's init() method has initialized the ODBC interface before the pollForEvents() method is called.

As a first step in the pollForEvents() method, a utility function allocates handles for two ODBC connections to the application database. The connector uses these connection handles to interact with the database.

The pollForEvents() method gets a pointer to the subscription manager using GenGlobals::getTheSubHandler(). It then executes a SQL SELECT statement using the ODBC SQLExecDirect() interface to determine what data is returned. When the SQL statement returns, the method allocates memory for the data, assigns the memory to specific columns of data, and retrieves the data one event record at a time into allocated memory.

For each event, the connector calls the subscription manager method isSubscribed() to determine whether there are subscribers to this particular business object and verb. If there are subscribers, the connector builds a new business object, sets the key, sets the business object's verb to Retrieve, and calls the business object's doVerbFor() method to retrieve the complete set of data for the application entity. If doVerbFor() returns success, the poll method sets the appropriate verb in the business object and sends the business object to InterChange Server using the method gotApplEvent().

Once the business object is sent, the poll method executes a SQL statement to remove the event from the event table. This action causes a delete trigger to store the event in the archive table.

Figure 75. Implementation of basic logic for pollForEvents()

// The event table is named cw_events.
 #define GET_EVENT_QUERY                                   \ 
       "SELECT event_id, object_name, object_verb, object_key \
       FROM cw_events WHERE status = 0 AND priority = 1 \
       ORDER BY event_time"
  
 #define REMOVE_EVENT_QUERY                                   \
       "DELETE FROM cw_events WHERE event_id = '%s'";
  
 int ExampleGlobals::pollForEvents()
 {
 SubscriptionHandlerCPP  *mySub;
 BusinessObject          *pObj = NULL;
 char                    *ev_id = 0; 
 char                    *obj_name = 0; 
 char                    *obj_verb = 0; 
 char                    *obj_key = 0; 
 char                    *query = 0;
 char                    *key_value = 0;
 char                    *cp;
 int                     i;
 RETCODE                 rc;
 HSTMT                   hstmt1 = 0;
 HSTMT                   hstmt2 = 0;
 SDWORD                  cbValue = SQL_NO_TOTAL;
 int                     retcode = BON_SUCCESS;
  
    // Private function for establishing and checking the two 
    // database connections that the poll function needs
    checkOdbcConnections();
  
    // Set up the subscription manager
    mySub = GenGlobals::getTheSubHandler();
  
    // Allocate a statement handle for the SQL statement 
    rc = SQLAllocStmt(gHdbc1, &hstmt1);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
       // handle odbc errors
       return (BON_APPRESPONSETIMEOUT);
    }
  
    // Execute the event SQL SELECT query to determine
    // what data will be returned. 
    // Use the macro GET_EVENT_QUERY
    rc = SQLExecDirect(hstmt1, GET_EVENT_QUERY, SQL_NTS);
    if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
       // handle odbc errors
       rc = SQLFreeStmt(hstmt1, SQL_CLOSE);
       return (BON_APPRESPONSETIMEOUT);
    }
  
    // Allocate memory for event table columns 
    ev_id = new char[80];
    obj_name = new char[80];
    obj_verb = new char[80];
    obj_key = new char[80];
  
    query = new char[255];
    key_value = new char[80];
 
   // Bind all results set columns to variables
    rc = SQLBindCol(hstmt1, 1, SQL_C_CHAR, ev_id,    80, &cbValue); 
    rc = SQLBindCol(hstmt1, 2, SQL_C_CHAR, obj_name, 80, &cbValue);
    rc = SQLBindCol(hstmt1, 3, SQL_C_CHAR, obj_verb, 80, &cbValue);
    rc = SQLBindCol(hstmt1, 4, SQL_C_CHAR, obj_key,  80, &cbValue);
  
    // Fetch the results
    while (rc == SQL_SUCCESS || rc != SQL_SUCCESS_WITH_INFO) {
       ev_id = '\0';
       obj_name = '\0';
       obj_verb = '\0';
       obj_key = '\0';
  
       rc = SQLFetch(hstmt1);
       if (rc == SQL_SUCCESS || rc != SQL_SUCCESS_WITH_INFO) {
          // Process the record
          // Trim off rest of string at the first 
          // space character found
          if ((cp = strchr(ev_id, ' ')) != NULL)
             *cp = NULL;
          if ((cp = strchr(obj_name, ' ')) != NULL)
             *cp = NULL;
          if ((cp = strchr(obj_verb, ' ')) != NULL)
             *cp = NULL;
           if ((cp = strchr(obj_key, ' ')) != NULL)
             *cp = NULL;
  
          // Determine whether there are subscribers to the event
          if (mySub->isSubscribed(obj_name, obj_verb) != TRUE) {
             // log message
             // delete event from event table
             // add event to archive table
             continue;
          }
  
          // Prepare to retrieve data into the business object
          pObj = new BusinessObject(obj_name);
          pObj->setVerb("Retrieve");
  
          // Get key:value pair
          if ((cp = strchr(obj_key, ':')) != NULL) {
             cp++;
             strcpy(key_value, cp);
             cp--;
             *cp = NULL;
          }
  
          // Find the first key in the object and set it
          for (i = 0; i < pObj->getAttrCount()-1; i++) {
             if (pObj->getSpecFor()->getAttribute(i)->isKey()) {
                   pObj->setAttrValue(pObj->getAttrName(i),
                   key_value, 
                   pObj->getAttrType(pObj->getAttrName(i)));
                   break;
             }
          }
 
         // Call the business object handler doVerbFor() 
          // to retrieve application data
          if (pObj->doVerbFor() == BON_FAIL) {
             // Log error message if retrieve fails
             // Handle retrieve errors
             retcode = BON_FAIL;
             break;
          }
  
          // Call gotApplEvent() to send the business object
          // with the info
          pObj->setVerb(obj_verb);
          if ((mySub->gotApplEvent(*pBusObj)) == BON_FAIL) {
                // Log error message
             retcode = BON_FAIL;
             break;
          }
  
          // Allocate statement handle for the SQL 
          // statement on the second connection
          rc = SQLAllocStmt(gHdbc2, &hstmt2);
          if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
             // Handle ODBC errors
             hstmt2 = (HSTMT)0;
             retcode = BON_APPRESPONSETIMEOUT;
             break;
          }
  
          // Remove the event from the event table
          // This will execute a trigger that will archive
          // the event in the archive table
          // Use the REMOVE_EVENT_QUERY macro.
          sprintf(query, REMOVE_EVENT_QUERY, ev_id);
          rc = SQLExecDirect(hstmt2, query, SQL_NTS);
          if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
             // Handle odbc errors
             retcode = BON_APPRESPONSETIMEOUT;
             break;
  
          } else {
                // Handle odbc errors
                retcode = BON_APPRESPONSETIMEOUT;
                break;
          }
       }
  
    // Clean up and free resources associated with
    // the statement handles
    if (hstmt1) {
       rc = SQLFreeStmt(hstmt1, SQL_DROP);
    }
    if (hstmt2) {
       rc = SQLFreeStmt(hstmt2, SQL_DROP);
    }
 
if (ev_id) {
       delete ev_id;
    }
    if (obj_name) {
       delete obj_name;
    }
    if (obj_verb) {
       delete obj_verb;
    }
    if (query) {
       delete query;
    }
    if (key_value) {
       delete key_value;
    }
  
    return (retcode);
 }
 

Copyright IBM Corp. 1997, 2004