Creating a business object handler

Creating a business object handler involves the following steps:

Note:
For an introduction to request processing, see "Request processing".. For a discussion of request processing and the implementation of doVerbFor(), see Request processing.

Extending the C++ business-object-handler base class

In the C++ connector library, the base class for a business object handler is named BOHandlerCPP. The BOHandlerCPP class provides methods for defining and accessing a business object handler. To implement your own business object handler, you extend this business-object-handler base class to create your own business-object-handler class.

Note:
For general information about the methods of the business-object-handler base class, see "Extending the business-object-handler base class"..

To derive a business-object-handler class for a C++ connector, follow these steps:

  1. Create a class that extends the BOHandlerCPP class. Name this class:
    connectorNameBOHandler.cpp
    

    where connectorName uniquely identifies the application or technology with which the connector communicates. For example, to create a business object handler for a Baan application, you can create a business-object-handler class called BaanBOHandler. If your connector design implements multiple business object handlers, include the name of the handled business objects in the name of the business-object-handler class.

  2. Implement the virtual method, doVerbFor(), to define the behavior of the business object handler. Other methods in the BOHandlerCPP class are already implemented. For more information on how to implement this virtual method, see "Implementing the doVerbFor() method".
    Note:
    The other methods in the BOHandlerCPP class have their implementations provided. The doVerbFor() method is the only virtual method in this class. For more information, see BOHandlerCPP class.

You might need to implement more than one business object handler for your connector, depending on the application and its API. For a discussion of some issues to consider when implementing business object handlers, see "Designing business object handlers"..

Implementing the doVerbFor() method

The doVerbFor() method provides the functionality for the business object handler. When the connector framework receives a request business object, it calls the doVerbFor() method in the appropriate business object handler to perform the action of this business object's verb. For a C++ connector, the BOHandlerCPP class defines the doVerbFor() virtual method. You must provide an implementation of this virtual method as part of your business-object-handler class.

Note:
For a general description of the role of the doVerbFor() method, see "Handling the request".. Figure 25 provides the method's basic logic.

The role of the business object handler is to perform the following tasks:

  1. Receive business objects from the connector framework
  2. Process each business object based on the active verb
  3. Send requests for operations to the application.
  4. Return status to the connector framework.

Table 53 summarizes the steps in the basic logic for the verb processing that the doVerbFor() method typically performs. Each of the sections listed in the For More Information column provides more detailed information on the associated step in the basic logic.

Table 53. Basic logic of the doVerbFor() method

Business-object-handler step For more information
1. Obtain the active verb from the request business object. "Obtaining the active verb"
2. Verify that the connector still has a valid connection to the application. "Verifying the connection before processing the verb"
3. Branch on the value of the valid active verb. "Branching on the active verb"
4. For a given active verb, perform the appropriate request processing:

  • Perform verb-specific tasks.

"Performing the verb operation"

  • Process the business object.

"Processing business objects"
5. Send the appropriate status to the connector framework. "Sending the verb-processing response"

In addition to the processing steps in Table 53,, this section also provides additional processing information in "Additional processing issues".

Obtaining the active verb

To determine which actions to take, the doVerbFor() method must first retrieve the verb from the business object that it receives as an argument. This incoming business object is called the request business object. The verb that this business object contains is the active verb, which must be one of the verbs that the business object definition supports. Table 54 lists the method that the C++ connector library provides to retrieve the active verb from the request business object.

Table 54. Method for obtaining the active verb

C++ connector library class Method
BusinessObject getVerb()

Obtaining the active verb from the request business object generally involves the following steps:

  1. Verify that the request business object is valid.

    Before the connector calls getVerb(), it should verify that the incoming request business object is not null. The incoming business object is passed into the doVerbFor() method as a BusinessObject object.

  2. Obtain the active verb with the getVerb() method.

    Once the request business object is valid, you can use the getVerb() method in the BusinessObject class to obtain the active verb from this business object.

  3. Verify that the active verb is valid.

    When the connector has obtained the active verb, it should verify that this verb is neither null nor empty.

If either the request business object or the active verb is invalid, the connector should not continue with verb processing. Instead, it should take the steps outlined in Table 55..

Table 55. Handling a verb-processing error

Error-handling step Method or code to use
1. Log an error message to the log destination to indicate the cause of the verb-processing error. BOHandlerCPP.logMsg(), BOHandlerCPP. generateAndLogMsg()
2. Set a message within the return-status descriptor to indicate the cause of the verb-processing failure. ReturnStatusDescriptor.seterrMsg()
3. Return a BON_FAIL outcome status from the doVerbFor() method. return BON_FAIL;

Figure 58 contains a fragment of the doVerbFor() method that obtains the active verb with the getVerb() method. This code ensures that the request business object and its active verb are not null. If either of these conditions exists, the code fragment stores a message in the return-status descriptor and exits with an outcome status of BON_FAIL.

Figure 58. Obtaining the active verb

int ExampleBOHandler::doVerbFor(BusinessObject &theObj,
      ReturnStatusDescriptor *rtnStatusMsg)
{
   int status = BON_SUCCESS;
 
   //make sure that the incoming business object is not null
   if (theObj == null) {
      generateAndLogMsg(1100, CxMsgFormat::XRD_ERROR, NULL, 0, NULL);
 
      char errorMsg[512];
      sprintf(errorMsg,
         "doVerbFor: Invalid request business object .");
      rtnStatusMsg->seterrorMsg(errorMsg);
      status = BON_FAIL;
   }
 
   // obtain the active verb
   char *verb = theObj.getVerb();
 
   // make sure the active verb is neither null nor empty
   if (verb == null || strcmp(verb, "")){
      generateAndLogMsg(6548, CxMsgFormat::XRD_ERROR, NULL, 0, NULL);
      sprintf(errorMsg,"doVerbfor: Invalid active verb.");
      rtnStatusMsg->seterrorMsg(errorMsg);
      status = BON_FAIL;
 
   // perform verb processing here
   ...
}

Verifying the connection before processing the verb

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 verb processing that doVerbFor() performs requires access to the application. Therefore, before the doVerbFor() method begins processing the verb, 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 verb processing. Instead, it should take the steps outlined in Table 56 to notify the connector framework of the lost connection.

Table 56. Handling a lost connection

Error-handling step Method or code to use
1. Log an error message to the log destination to indicate the cause of the verb-processing error. The connector logs a fatal error message so that email notification is triggered if the LogAtInterchangeEnd connector configuration property is set to True. BOHandlerCPP.logMsg(), BOHandlerCPP.generateAndLogMsg()
2. Set a message within the return-status descriptor to indicate the cause of the lost connection. ReturnStatusDescriptor.seterrMsg()
3. Return the BON_APPRESPONSETIMEOUT outcome status from the doVerbFor() method. return BON_APPRESPONSETIMEOUT;

Note:
This return-status descriptor object is part of the verb-processing response that doVerbFor() sends to the connector framework. For information on these methods, see ReturnStatusDescriptor class.

After the connector returns BON_APPRESPONSETIMEOUT to inform the connector controller that the application is not responding, it stops the process in which the connector runs. A system administrator must fix the problem with the application and restart the connector to continue processing events and business object requests.

Figure 59 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 59. Example of loss of connection in doVerbFor()

int ExampleBOHandler::doVerbFor(BusinessObject &theObj,
      ReturnStatusDescriptor *rtn)
{
   ...
   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
   ...
} 

Branching on the active verb

The main task of verb processing is to ensure that the application performs the operation associated with the active verb. The action to take on the active verb depends on whether the doVerbFor() method has been designed as a basic method or a metadata-driven method:

Basic verb processing

For verb-processing that is not metadata-driven, you branch on the value of the active verb to perform the verb-specific processing. Your doVerbFor() method must handle all verbs that the business object supports.

Note:
As part of the verb-branching logic, make sure you include a test for an invalid verb. If the request business object's active verb is not supported by the business object definition, the business object handler must take the appropriate recovery actions to indicate an error in verb processing. For a list of steps to handle a verb-processing error, see Table 55..

Figure 60 shows a basic doVerbFor() method that handles create, update, retrieve, and delete operations. This code that branches off the active verb's value for the Create, Update, Retrieve, and Delete verbs. For each verb your business object supports, you must provide a branch in this code. It then calls the corresponding verb method to continue the business object processing.

At the top of this code fragment, this C++ doVerbFor() method defines special constants to identify the different verbs. Use of these verb constants make it easier to identify the active verbs in the code as well as to change their string representations. If your connector handles additional verbs, IBM recommends that you define String constants as part of your extended BOHandlerCPP class.

Figure 60. Branching on the active verb's value

#define CREATE "Create"
#define UPDATE "Update"
#define RETRIEVE "Retrieve"
#define DELETE "Delete"
 
int ExampleBOHandler::doVerbFor(BusinessObject &theObj,
      ReturnStatusDescriptor *rtnStatusMsg)
{
   int status = BON_SUCCESS;
 
   // Determine the verb of the incoming business object
   char *verb = theObj.getVerb();
 
   if (strcmp(verb, CREATE) == 0)
      status = doCreate(theObj);
   else if (strcmp(verb, UPDATE) == 0)
      status = doUpdate(theObj);
   else if (strcmp(verb, RETRIEVE) == 0)
      status = doRetrieve(theObj);
   else if (strcmp(verb, DELETE) == 0)
      status = doDelete(theObj);
   else
   {
      // This verb is not supported.
      // Send the collaboration a message to that effect
      // in the ReturnStatusDescriptor object.
      char errorMsg[512];
      sprintf(errorMsg,"doVerbFor: verb '%s' is not supported ",
         verb);
      rtnStatusMsg->setErrorMsg(errorMsg);
      status = BON_FAIL;
   }
 
   // Return status to connector framework
   return status;
}

The code fragment in Figure 60 is modularized; that is, it puts the actual processing of each supported verb into a separate verb method: doCreate(), doUpdate(), doRetrieve(), and doDelete(). Each verb method should meet the following minimal guidelines:

This modular structure greatly simplifies the readability and maintainability of the doVerbFor() method.

Metadata-driven verb processing

For metadata-driven verb-processing method, the application-specific information for the verb contains metadata, which provides processing instructions for the request business object when that particular verb is active. Table 57 lists the method that the C++ connector library provides to obtain application-specific information for the verb of a business object.

Table 57. Method for retrieving the verb's application-specific information

C++ connector library class Method
BusObjSpec getVerbAppText()

The verb application-specific information can contain the name of the method to call to process the request business object for that particular verb. In this case, the doVerbFor() method does not need to branch off the value of the active verb because the processing information resides in the verb's application-specific information.

Figure 61 shows a forms-based, metadata-driven doVerbFor() method that implements all verb processing for a business object. Using the business object application-specific information, the method identifies a form name and loops through the business object attributes to retrieve attribute descriptions. Each attribute description is an instance of the BOAttrType class. Through this class, the method can obtain attribute application-specific information and other information about the attribute, such as whether it is a key.

Note:
For more information on how to process business objects, see "Processing business objects".

The method retrieves the attribute values from the business object instance, and fills in the form using the attribute metadata to identify the fields of the form for each attribute. The method identifies the verb operation in the business object, retrieves the verb metadata to get any processing instructions, and sends the complete form to the application. If this is a Create operation and the application creates new data, such as keys, the method retrieves the data from the application and processes it.

Figure 61. Metadata-driven verb processing

int ExampleBOHandler::doVerbFor(BusinessObject &theObj, 
      ReturnStatusDescriptor *rtnObj)
{
   BusObjSpec                  *theSpec;
   int                status = BON_SUCCESS;
 
   // Get the business object definition and its metadata:
   // the name of the form. Open the specified form 
   theSpec = theObj.getSpecFor();
   form = OpenForm(theObj.getAppText());
 
   // For each attribute, retrieve the attribute description, 
   // get the attribute values and application-specific information, 
   // and set the field of the form 
   for (int i = 0; i < theObj.getAttrCount; i++) { 
      BOAttrType * curAttr = theObj.getAttrDesc(i); 
      Form.setfield(curAttr->getAppText(),theObj.getAttrValue(i));
   }
 
   // Get the verb and the verb metadata: the type of operation
   // to perform. Tell the application to do the operation
   Form.doOperation(theSpec->getVerbAppText(theObj.getVerb()));
 
   // Process returned attributes if any
   for (int k=O; k < theObj.getAttrCount() -1; k++) {
      BOAttrType * curAttr = theObj.getAttrDesc(k);
      value = Form.getField(curAttr->getAppText(); 
      theObj.setAttrValue(k, value);
   }
   return status;
}

Note:
Another use of verb application-specific information can be to specify the application's API method to call to update the application entity for the particular verb.

Performing the verb operation

Table 58 lists the standard verbs that a doVerbFor() method can implement, as well as an overview of how each verb operation processes the request business object. For more information on processing business objects, see "Processing business objects".

Table 58. Performing the verb operation

Verb Use of request business object For more information
Create
  • Use any application-specific information in the business object definition to determine in which application structure to create the entity (for example, a database table).
  • Use any application-specific information for each attribute to determine in which application substructure to add the attribute values (for example, a database column).
  • Use attribute values as values to save in new application entity.

If the application generates key values for the new entity, save the new key values in the request business object, which should then be included as part of the verb-processing response.

"Handling the Create verb"
Retrieve
  • Use any application-specific information in the business object definition to determine from which application structure (for example, a database table) to retrieve the entity.
  • Use attribute key value (or values) to identify which application entity to retrieve.

If the application finds the requested entity, save its values in the request business object's attributes. The request business object should then be included as part of the verb-processing response.

"Handling the Retrieve verb"
Update
  • Use any application-specific information of the business object definition to determine in which application structure (for example, a database table) to update the entity.
  • Use any application-specific information for each attribute to determine which application substructure to update with the attribute values (for example, a database column).
  • Use attribute key value (or values) to identity which application entity to update.
  • Use the attribute values as values to update the existing application entity.

If the application is designed to create an entity if the one specified for update does not exist, save the new entity values in the request business object's attributes. The request business object should then be included as part of the verb-processing response.

"Handling the Update verb"
Delete
  • Use any application-specific information in the business object definition to determine from which application structure (for example, a database table) to delete the entity.
  • Use attribute key value (or values) to identify which application entity to delete.

The request business object should then be included as part of the verb-processing response so that InterChange Server can perform any required cleanup of relationship tables.

"Handling the Delete verb"

Processing business objects

Most verb operations involve obtaining information from the request business object. This section provides information about the steps your doVerbFor() method needs to take to process the request business object.

Note:
These steps assume that your connector has been designed to be metadata-driven; that is, they describe how to extract application-specific information from the business object definition and attributes to obtain the location within the application associated with each attribute. If your connector is not metadata-driven, you probably do not need to perform any steps that extract application-specific information.

Table 59 summarizes the steps in the basic program logic for deconstructing a request business object that contains metadata.

Table 59. Basic logic for processing a request business object with metadata

Step For more information
1. Obtain the business object definition for the request business object. "Accessing the business object definition"
2. Obtain the application-specific information in the business object definition to obtain the application structure to access. "Extracting business object application-specific information"
3. Obtain the attribute information. "Accessing the attributes"
4. For each attribute, get the attribute application-specific information in the business object definition to obtain the application substructure to access. "Extracting attribute application-specific information"
5. Make sure that processing occurs only for those attributes that are appropriate. "Determining whether to process an attribute"
6. Obtain the value of each attribute whose value needs to be sent to the application entity. "Extracting attribute values from a business object"
7. Notify the application to perform the appropriate verb operation. "Initiating the application operation"
8. Save any attribute values in the request business object that are required for the verb-processing response. "Saving attribute values in a business object"

The section walks through the basic logic of an example Create method, explaining in detail how it works. This example verb method uses the basic program logic in Table 59 to deconstruct a business object and build an ODBC SQL command. To see the complete verb method, go to "Example: Create method for a flat business object".

Accessing the business object definition

For a C++ connector, the doVerbFor() method receives the request business object as an instance of the BusinessObject class. However, to begin verb processing, the doVerbFor() method often needs information from the business object definition, which is an instance of the BusObjSpec class. Therefore, the first step in a typical C++ verb operation is to retrieve the pointer to the business object definition for the request business object.

Table 60 lists the method that the C++ connector library provides to obtain the business object definition for the current business object (a BusinessObject instance).

Table 60. Method for obtaining a business object definition

C++ connector library class Method
BusinessObject
getSpecFor()

Suppose a verb method called doSimpleCreate() implements processing for the Create verb for a table-based application. Figure 62 shows one way to call getSpecFor() to obtain the business object definition (theSpec) for the request business object (theObj).

Figure 62. Obtaining the business object definition

int doSimpleCreate(BusinessObject &theObj)
{
   ...
   BusObjSpec *theSpec = theObj.getSpecFor()

Note:
At connector startup, the connector instantiates BusObjSpec instances for all business object definitions that the connector supports. The getSpecFor() method returns a pointer to the instance of the business object definition associated with the request business object.

Once getSpecFor() obtains a reference to the BusObjSpec instance, the doVerbFor() method can use methods of the BusObjSpec class to obtain information from the business object definition, such as its application-specific information and access to the attribute descriptors. The business object definition includes the information shown in Table 61.. For a complete list of BusObjSpec methods, see BusObjSpec class.

Table 61. Methods for obtaining information from the business object definition

Business object definition information BusObjSpec method
The name of the business object definition getName()
A verb list--contains the verbs that the business object supports. isVerbSupported()
A list of attributes--for each attribute, the BusObjSpec object provides: getAttributeCount()
  • position in the list of attributes

getAttributeIndex()

getAttribute()
Application-specific information:
  • business object definition

getAppText()
  • verb

getVerbAppText()
Note: Access to application-specific information for an attribute is provided in the BOAttrType class.

A business object handler typically uses the business object definition to get information on the business object's attributes or to get the application-specific information from the business object definition, attribute, or verb.

Extracting business object application-specific information

Business objects for metadata-driven connectors are often designed to have application-specific information that provides information about the application structure. For such connectors, a typical verb operation must retrieve the application-specific information from the business object definition associated with the request business object. Table 62 lists the method that the C++ connector library provides to retrieve application-specific information from the business object definition.

Table 62. Method for obtaining business object application-specific information

C++ connector library class Method
BusObjSpec getAppText()

Note:
The method to obtain application-specific information, shown in Table 62,, uses deprecated terminology in its method name. This method name refers to "application-specific text". The more current name for "application-specific text" is "application-specific information".

As Table 62 shows, the connector uses the getAppText() method to obtain the application-specific information for the business object definition.

char * appInfo = theSpec->getAppText());

The getAppText() method retrieves a character string containing the application-specific information from the business object definition. Using the example business object shown in Figure 38,, the preceding line of code copies the table name customer into the variable appText.

For the doSimpleCreate() method in Figure 62,, the verb method implements processing for the Create verb for a a table-based application. For such an application, the business objects have usually been designed to have application-specific information provide the verb operations with information about the application structure (For more information, see Table 36). The application-specific information in a business object definition can contain the name of the database table associated with the business object.

The verb method first accesses application-specific information through the business object definition. Therefore, the verb method calls BusObjSpec::getAppText() to obtain the name of the database table to access. The connector can then use the retrieved table name to begin building the SQL statement that accesses the application database. For a Create operation, the SQL statement is INSERT.

Using the example Customer business object shown, the code fragment in connector Figure 63 constructs an INSERT statement that adds a new row to an application database table named customer. At this point in the execution of the verb method, this SQL statement is:

INSERT INTO customer

Figure 63. Obtaining the name of the database table

int doSimpleCreate(BusinessObject &theObj)
{
   char                          table_name[64];
   char                          insertStatement[1024]; 
   BusObjSpec                     *theSpec;
 
   // Retrieve pointer to the business object definition
   theSpec = theObj.getSpecFor();
 
// Retrieve the table name from the AppSpecificInfo property
// for the business object definition
strcpy(table_name, theSpec->getAppText());
 
// Begin building the SQL INSERT statement
sprintf(insertStatement, "INSERT INTO %s (", table_name);
...
}

Accessing the attributes

For a C++ connector, the doVerbFor() method receives the request business object as an instance of the BusinessObject class. However, if the verb operation needs to obtain information about attribute properties, it needs to access an attribute descriptor, which is an instance of the BOAttrType class. Therefore, a typical C++ verb operation must retrieve a pointer to each attribute descriptor that it needs to access in the request business object.

Table 63 lists the methods that the C++ connector library provides to obtain the attribute descriptors from the current business object.

Table 63. Classes and methods for obtaining an attribute descriptor

C++ connector library class Method
BusObjSpec getAttribute(),, getAttributeCount(),, getAttributeIndex()
BusinessObject getAttrDesc(),, getAttrCount()

To access an attribute descriptor, the connector can use either of the following methods:

The getAttribute() and getAttrDesc() methods can access an attribute descriptor in one of two ways:

Once the attribute descriptor exists, the connector can use methods of the BOAttrType class to obtain information about the properties of the associated attribute, such as its cardinality or maximum length. Table 64 lists the methods that the C++ connector library provides to retrieve information from an attribute descriptor. For a complete list of methods in the BOAttrType class, see BOAttrType class.

Table 64.

Methods for obtaining information about attribute properties
Attribute property BusObjAttr method
Name getName(),, hasName()
Type getRelationType(),, getTypeName(),, getTypeNum(),, hasTypeName(),, isObjectType(),, isType()
Key isKey()
Foreign key isForeignKey()
Max Length getMaxLength()
Required isRequired()
Cardinality getCardinality(),, hasCardinality(),, isMultipleCard()
Default Value getDefault()
Attribute application-specific information getAppText()

Important:
The attribute value is not available from within the attribute descriptor (BOAttrType instance) in the business object definition. You must access an attribute value through the attribute in the BusinessObject instance. For more information, see "Extracting attribute values from a business object".

Extracting attribute application-specific information

If business objects for metadata-driven connectors are designed to have application-specific information that provides information about the application structure, the next step after extracting the application-specific information from the business object definition is to extract the application-specific information from each attribute in the request business object. Table 65 lists the method that the C++ connector library provides to retrieve application-specific information from an attribute descriptor.


Table 65. Method for obtaining attribute application-specific information

C++ connector library class Method
BOAttrType getAppText()

Note:
The method to obtain application-specific information, shown in Table 65,, uses deprecated terminology in its method name. This method name refers to "application-specific text". The more current name for "application-specific text" is "application-specific information".

The connector uses the getAppText() method to obtain the application-specific information for an attribute. If business objects have been designed to have application-specific information provide information for a table-based application, the application-specific information for the attribute contains the name of the application table's column associated with this attribute (For more information, see Table 36).

Using the example business object shown in Figure 38,, the code fragment in connector Figure 63 begins construction of an INSERT statement that adds a new row to an application database table named customer. After extracting the application-specific information from the business object definition, the next step is to determine what columns in the application table will be updated by the business object request. The connector can then use the retrieved column name to continue building the SQL statement. It would append each column name to the column list of the INSERT statement that adds a new row to an application database table named customer. After all attributes are processed, this SQL statement is:

INSERT INTO customer (cust_key, cust_name, cust_status, cust_region)

For a C++ connector, the verb method calls BusinessObject::getAttrCount() on the current business object to determine the number of attributes in a business object. To obtain the application-specific information for each attribute involves the following steps:

  1. The verb method then traverses the business object definition and calls BusObjSpec::getAttribute() to retrieve each attribute descriptor.

    The getAttribute() method returns a pointer to an instance of the BOAttrType class. Each BOAttrType instance represents a single attribute descriptor for an attribute in a business object definition.

  2. Through the attribute descriptor, the connector can retrieve information about attribute properties, such as whether the attribute is a key or foreign key.

    For each attribute, the method extracts the application-specific information for the attribute from the attribute descriptor with the getAppText() method, which is defined in the BOAttrType class.

In Figure 64,, the verb method copies the column name for each attribute into the column variable as it traverses through the business object.

Figure 64. Obtaining attribute application-specific information

for (i = 0; i < theObj.getAttrCount() - 1; i++) 
   strcpy(column, theSpec->getAttribute(i)->getAppText()); 

The for loop in Figure 64 performs the following tasks:

Determining whether to process an attribute

Up to this point, the verb processing has been using the application-specific information to obtain the application location for each attribute of the request business object. Once it has this location information, the doVerbFor() method can begin processing the attribute.

As the verb operation loops through the business object attributes, you might want to confirm that the method processes only certain attributes. Table 66 lists some of the methods that the C++ connector library provides to determine whether an attribute should be processed.


Table 66. Classes and methods for determining attribute processing

Attribute test C++ connector library class and method
An attribute is a simple attribute and not an attribute that represents a contained business object. BOAttrType

isObjectType()
The value of the attribute in the business object instance is not the special value of Blank (a zero-length string) or Ignore (a null pointer). BusinessObject

getAttrValue(),, isIgnoreValue(),, isIgnore(),, isBlankValue(),, isBlank()
The attribute is not a place-holder attribute. Place-holder attributes are used in business object definitions to separate attributes that contain child business objects. BOAttrType

getAppText()

Using the methods in Table 66,, a verb operation can determine that an attribute is one that the operation wants to process:

The code fragment in Figure 65 shows how the sample Create verb method determines that an attribute is one that the method wants to process. The method first gets the attribute value from the current business object by calling BusinessObject::getAttrValue(). It then performs the tests to determine if the attribute should be processed.

Figure 65. Determining whether to process an attribute

for (i = 0; i< theObj.getAttrCount()-1; i++) {
   theAttr = theSpec->getAttribute(i);
   theAttrVal = theObj.getAttrValue(i);
 
   if (!theAttr->isObjectType() && strlen(theAttr->getAppText()) > 0)
   {
      // Use only columns that contain a valid value 
      if (!(theObj.isIgnoreValue((char *)theAttrVal)) &&
            !(theObj.isBlankValue((char *)theAttrVal))) {
 
         // Get the column name from the AppSpecificInfo text
         strcpy(column, theSpec->getAttribute(i)->getAppText()); 
   }
}

For single attributes that have application-specific information and that contain values that are not Ignore or Blank, the connector retrieves the column name from the attribute application-specific information in the business object definition and appends the names to the SQL statement.

Extracting attribute values from a business object

Once the verb operation has confirmed that the attribute is ready for processing, it usually needs to extract the attribute value:

The value of an attribute is part of the attribute information in the business object (BusinessObject instance). Table 67 lists the methods that the C++ connector library provides to obtain attribute values from a business object.

Table 67. Methods for obtaining attribute values

C++ connector library class Method
BusinessObject getAttrCount(),, getAttrValue()

As in the business object definition, each attribute in the business object can be accessed in one of two ways:

As Table 67 shows, the BusinessObject class provides a single method for obtaining attribute values of all valid data types, getAttrValue(). Because the type of an attribute in a business object definition can be any supported type, the return value of getAttrValue() is defined as a void pointer. You should check the type of the attribute in the business object definition, and based on the attribute type, cast the void pointer to a character pointer, a business object pointer, or a business object array before you assign the returned value to a variable.

Note:
Attribute values that are neither business objects nor business object arrays are stored as pointers to character strings in the C++ connector library. If the value of an attribute is not a business object or business object array, you need to cast the void pointer to a character pointer.

After identifying the attributes to process, the doSimpleCreate() method (see Figure 62,, Figure 63,, and Figure 65) must obtain the data values to insert into columns in the application table. As the method processes each attribute, it adds the attribute value to the SQL statement. To create the list of attribute values, the verb method traverses the attributes of the business object definition a second time. For each attribute, it obtains the attribute value from the business object instance. In this second traversal of the business object, the verb method again checks the type and value of each attribute to determine whether it wants to process the attribute.

Note:
Although this connector traverses a business object twice to construct a database query, if you are using an application API to set values in the application, the API might not need to loop through the business object in this way.

The connector can then use the retrieved column value to continue building the SQL statement. Using the example business object shown in Figure 38,, the connector would append each column value to the VALUES clause of the INSERT statement that adds a new row to an application database table named customer.

Suppose the doSimpleCreate() method processed a sample Customer business object with the following data:

CustomerId 87975
CustomerName Trievers Inc.
CustomerStatus 3
CustomerRegion NE

After all attributes are processed, this SQL statement might be:

INSERT INTO customer (cust_key, cust_name, cust_status, cust_region)
   VALUES (87975, 'Trievers Inc.', 3, 'NE')

For a C++ connector, the verb method calls BusinessObject::getAttrValue() to retrieve the value of each attribute from the business object instance. The verb method casts returned attribute values to character pointers to generate the VALUES clause of the INSERT statement. Figure 66 shows a code fragment of the Create verb method that accesses the attribute values and appends them to the VALUES clause of the INSERT statement.

Figure 66. Accessing the attribute values in a C++ connector

for (i = 0; i< theObj.getAttrCount()-1; i++) {
   theAttr = theSpec->getAttribute(i);
   theAttrVal = theObj.getAttrValue(i);
 
   // Process simple attributes 
   if (!theAttr->isObjectType() && strlen (theAttr->getAppText()) > 0)
   {
 
      // Use columns that contain a valid value in
      // the business object
      if (!(theObj.isIgnoreValue((char *)theAttrVal)) &&
            !(theObj.isBlankValue((char *)theAttrVal))) {
 
         // Set the quote character for attributes that
         // are STRING type
         quote_str[0] = (theObj.getAttrType(i) ==
                     BOAttrType::STRING) ? '\'' : ' ';
 
         // Build the value and add it to insertStatement
         sprintf(clause, "%s %s%s%s", firstLoop ? " " : ",",
                  quote_str, (char *)theAttrVal, quote_str);
         strcat(insertStatement, clause);
         firstLoop = 0;
      }
   }
}

Initiating the application operation

Once the verb operation has obtained the information it needs from the request business object, it is ready to send the application-specific command so that the application performs the appropriate operation. The command must be appropriate for the verb of the request business object. For a table-based application, this command might be an SQL statement or a ODBC call. Consult your application documentation for more information.

Important:
Your doVerbFor() method must ensure that the application operation completes successfully. If this operation is unsuccessful, the doVerbFor() method must return the appropriate outcome status (such as BON_FAIL) to the connector framework. For more information, see "Sending the verb-processing response".

When the doSimpleCreate() method has built the SQL statement, it is ready to execute it. When the INSERT statement is executed, the application creates a new row in the customer database table. To execute the SQL statement, you must use the application API that provides table access. The doSimpleCreate() verb method uses the standard ODBC API to execute the SQL statement. If your application has an API that executes SQL statements, use the application API. The code fragment in Figure 67 finishes the SQL statement and executes it using an ODBC call.

Figure 67. Executing the INSERT statement in a C++ connector

// Finish the INSERT statement
strcat(insertStatement, ")");
 
// Allocate an ODBC statement 
rc = SQLAllocStmt(hdbc, &hstmt); 
// Execute the SQL statement
rc = SQLExecDirect(hstmt, (unsigned char *)insertStatement, SQL_NTS); 
// Free the ODBC statement handle

Saving attribute values in a business object

Once the application operation has completed successfully, the verb operation might need to save new attribute values retrieved from the application into the request business object:

Table 68 lists the methods that the C++ connector library provides to save attribute values in a business object.

Table 68. Methods for saving attribute values

C++ connector library class Method
BusinessObject getAttrCount(),, setAttrValue()

An attribute in the business object can be accessed by its name or its index (its ordinal position). You can obtain a count of all attributes in the business object definition with getAttrCount() and loop through them one at a time, passing each index value to setAttrValue() to get an attribute value.

As Table 68 shows, the BusinessObject class provides a single method for saving attribute values of all valid data types: setAttrValue(). Because the type of an attribute in a business object definition can be any supported type, the parameter value of setAttrValue() is defined as a void pointer.

Sending the verb-processing response

The C++ connector must send a verb-processing response to the connector framework, which in turn sends a response to the integration broker. This verb-processing response includes the following information:

The following sections provide additional information about how a C++ connector provides each of the pieces of response information. For general information about the connector response, see "Indicating the connector response"..

Returning the outcome status

The doVerbFor() method provides an integer outcome status as its return code. As Table 69 shows, the C++ connector library provides constants for the outcome-status values that doVerbFor() is mostly likely to return.

Important:
The doVerbFor() method must return an integer outcome status to the connector framework.

Table 69. Outcome-status values for a C++ doVerbFor()

Condition in doVerbFor() C++ outcome status
The verb operation succeeded. BON_SUCCESS
The verb operation failed. BON_FAIL
The application is not responding. BON_APPRESPONSETIMEOUT
At least one value in the business object changed. BON_VALCHANGE
The requested operation found multiple records for the same key value. BON_VALDUPES
The connector finds multiple matching records when retrieving using non-key values. The connector will only return the first matching record in a business object. BON_MULTIPLE_HITS
The connector was not able to find matches for Retrieve by non-key values. BON_FAIL_RETRIEVE_BY_CONTENT
The requested business object entity does not exist in the database. BON_BO_DOES_NOT_EXIST

Note:
The C++ connector library provides additional outcome-status constants for use by other connector methods. For a complete list of outcome-status constants, see Table 69..

The outcome status that doVerbFor() returns depends on the particular active verb it is processing. Table 70 lists the tables in this manual that provide possible return values for the different verbs.

Table 70. Return values for different verbs

Verb For more information
Create Table 28
Retrieve Table 29
RetrieveByContent Table 30
Update Table 32
Delete Table 34
Exist Table 35

Using the outcome status that doVerbFor() returns, the connector framework determines its next action:

Populating the return-status descriptor

The return-status descriptor is a structure that holds additional information about the state of the doVerbFor() method when this method exits. The connector framework passes in an empty return-status descriptor as an argument to doVerbFor(). The doVerbFor() method can update this return-status descriptor with a message. This updated return-status descriptor is then accessible by the connector framework when doVerbFor() exits. The connector framework then includes the return-status descriptor in the response it sends to the integration broker.

WebSphere InterChange Server

If your business integration system uses InterChange Server, the connector framework returns the response to the connector controller, which routes it to the collaboration.

For a C++ connector, the return-status descriptor is a ReturnStatusDescriptor object. Table 71 lists the status information that this structure provides.

Table 71. Information in the return-status descriptor

Return-status descriptor information Description C++ accessor method
Error message A string to provide a description of the error condition getErrorMsg(),, seterrMsg()
Status value An integer status value to detail the cause of the error condition getStatus(),, setStatus()

The return-status descriptor is filled in one of two ways:

For example code that fills the return-status descriptor before exiting, see Figure 58..

Updating the request business object

The connector framework passes in the request business object as an argument to doVerbFor(). The doVerbFor() method can update this business object with attribute values. This updated business object is then accessible by the connector framework when doVerbFor() exits.

The connector framework uses the outcome status to determine whether to return a business object as part of the connector's response, as follows:

Important:
The value that the doVerbFor() method returns affects what the connector framework sends to InterChange Server. If the value is BON_VALCHANGE or BON_MULTIPLE_HITS, the connector framework returns a changed business object. You must ensure that the request business object is updated as appropriate for the returned outcome status.

Example: Create method for a flat business object

The C++ code sample in Figure 68 shows a Create method that uses the Open Database Connectivity (ODBC) API to insert a new record in an application database. The ODBC interface is a standard API for accessing a variety of database systems.

This code sample illustrates the basic logic of extracting information from a business object. It shows how the connector can use the metadata in the business object definition and the content of the business object instance to build a SQL INSERT statement.

The connector first calls BusinessObject::getSpecFor() to retrieve a pointer to the business object definition for the business object instance passed in as an argument to the Create method. Using BusObjSpec::getAppText(), the connector retrieves the name of the application table from the business object definition application-specific information and begins building the SQL statement. For each attribute in the business object instance that is a value other than the special Blank or Ignore value, the connector retrieves the column name from the attribute application-specific information in the business object definition and appends it to the SQL statement.

The connector then calls BusinessObject::getAttrValue() to retrieve the value of each attribute from the business object instance. When the SQL INSERT statement is complete, the method calls the ODBC API SQLExecDirect() to submit the statement. Typically, a Create method gets keys for new entities from an application, returns the keys to InterChange Server in a business object, and returns BON_VALCHANGE. However, because this method sets the key to the value in the source application, it simply returns BON_SUCCESS.

Figure 68. Example Create method

int doSimpleCreate(BusinessObject &theObj)
{
   char                          table_name[64];
   char                          column[64];
   char                          columnList[256];
   char                          clause[256];
   char                          insertStatement[1024]; 
   char                          quote_str[2] = " ";
   int                           firstLoop = 1;
   int                           j;
   BusObjSpec                     *theSpec;
   void                          *theAttrVal;
   BOAttrType                    *theAttr;
   RETCODE                       rc; /* return code for ODBC functions */
   HSTMT                         hstmt; /* pointer to ODBC statement handle */
 
   // Retrieve pointer to the business object definition
   theSpec = theObj.getSpecFor();
 
   // Retrieve the table name from the AppSpecificInfo property
   // for the business object definition
   strcpy(table_name, theSpec->getAppText());
   // Begin building the SQL INSERT statement
   sprintf(insertStatement, "INSERT INTO %s (", table_name);
 
   // Build the list of column names for the INSERT statement
   // For each attribute, extract the column name from the
   // attribute AppSpecificInfo property
   for (j = 0; j < theObj.getAttrCount()-1; j++) {
      theAttr = theSpec->getAttribute(j);
      theAttrVal = theObj.getAttrValue(j);
 
      // Process non-child objects only
      if (!theAttr->isObjectType() && 
               strlen (theAttr->getAppText()) > 0) {
         // Use only columns that contain a valid value 
         // in the Business Object
         if (!(theObj.isIgnoreValue((char *)theAttrVal)) &&
               !(theObj.isBlankValue((char *)theAttrVal))) {
            // Get the column name from the AppSpecificInfo text
            strcpy(column, 
               theSpec->getAttribute(j)->getAppText()); 
 
            sprintf(columnList, "%s %s", firstLoop ? " " : ",",
               column);
            strcat(insertStatement, columnList);
 
            firstLoop = 0;
         }
      }
   }
 
   // Add the VALUES SQL keyword
   sprintf(clause, ") VALUES (");
   strcat(insertStatement, clause);
 
   // Build the values to be inserted
   // For each attribute, extract the value from the business object
   firstLoop = 1;
   for (j = 0; j < theObj.getAttrCount()-1; j++) {
      theAttr = theSpec->getAttribute(j);
      theAttrVal = theObj.getAttrValue(j);
 
      // Process non-child objects only
      if (!theAttr->isObjectType() && 
            strlen (theAttr->getAppText()) > 0) {
 
         // Use columns that contain a valid value in 
         // the business object
         if (!(theObj.isIgnoreValue((char *)theAttrVal)) &&
                  !(theObj.isBlankValue((char *)theAttrVal))) {
 
            // Set the quote character if this is a STRING attribute
            quote_str[0] = (theObj.getAttrType(j) ==
               BOAttrType::STRING) ? '\'' : ' ';
 
            // Build the value and add it to insertStatement
            sprintf(clause, "%s %s%s%s", firstLoop ? " " : ",",
               quote_str, (char *)theAttrVal, quote_str);
            strcat(insertStatement, clause);
            firstLoop = 0;
         }
      }
   }
 
   // Finish the INSERT statement
   strcat(insertStatement, ")"); 
   // Allocate an ODBC statement 
   rc = SQLAllocStmt(hdbc, &hstmt);
   // Execute the SQL statement
   rc = SQLExecDirect(hstmt, (unsigned char *)insertStatement,
      SQL_NTS); 
   // Free the ODBC statement handle
   SQLFreeStmt(hstmt, SQL_DROP);
 
   return BON_SUCCESS; 
}

Note:
The code sample in Figure 68 shows a general approach to metadata-driven connector design. However, much of the example is specific to an ODBC-based connector. The ODBC (Open Database Connectivity) API was used because it is a standard API for accessing a database. If your application provides an API that allows a connector to modify application data, it is best to use the application API. When using an application API, the implementation of verb operations might differ from the implementation shown in this example.

Additional processing issues

This section provides the following additional information about how to process the request business object:

Handling the Blank and Ignore values

In addition to a regular attribute value, simple attributes in business objects can have either of the special values shown in Table 72..

Table 72. Special attribute values for simple attributes

Special attribute value Represents
Blank A zero-length string value
Ignore A value that the connector should ignore
WebSphere InterChange Server

Important:
If your business integration system uses InterChange Server, in the third-party maps, the string CxIgnore represents an Ignore value, and the string CxBlank represents a Blank value. These strings should be used only in maps. They should not be stored in business objects as attribute values because they are reserved keywords in the InterChange Server system.

The connector can call C++ connector library methods to determine whether a business object attribute is set to a special value:

When a connector creates a new business object, all attribute values are set to Ignore internally. A connector must set appropriate values for attributes, since all unset attribute values remain defined as Ignore. To set attribute values to the special Ignore or Blank values, you use the setAttrValue() method (defined in the BusinessObject class), passing it a special attribute-value constant, as follows:

Blank constant BusinessObject::BlankValue
Ignore constant BusinessObject::IgnoreValue

For example, the following C++ code fragment sets all non-key attributes to the Ignore value.

for (i = 0; i < theObj.getAttrCount()-1; i++) 
   {
   if (!theAttr->isKey()) 
      {
      attrname = theObj.getAttrName(i);
      theObj.setAttrValue(attrname, BusinessObject::IgnoreValue,
         theObj.getAttrType(i));
      }
   }

As another example, the C++ code fragment below retrieves application data and sets business object attributes that have NULL values in the application database to the Blank attribute value.

// Fetch application data into appdata variable
// Process record
for (i = 0; i < theObj.getAttrCount()-1; i++) 
   {
   if (!theSpec->getAttribute(i)->isObjectType()) 
      {
      if (strlen(appdata)==0) 
         sprintf(attrValue, theObj.getBlankValue());
      theObj.setAttrValue(i, (void*)attrValue,theObj.getAttrType(i));
      }
   }

Accessing child business objects

As discussed in "Processing hierarchical business objects",, a C++ connector uses the methods of the C++ connector library shown in Table 77 to access a child object.

Table 77. Classes and methods for accessing child business objects

C++ connector library class Method
BOAttrType
isObjectType(),, isMultipleCard() OBJECT attribute-type constant
BusinessObject
getAttrValue()
BusObjContainer
getObjectCount(),, getObject()

The verb processing in the doVerbFor() method uses the isObjectType() method to determine if the attribute contains a business object (its attribute type is set to the OBJECT attribute-type constant). When doVerbFor() finds an attribute that is a business object, the method checks the cardinality of the attribute using isMultipleCard(). Based on the results of isMultipleCard(), the method takes one of the following actions:

The return value of getAttrValue() must be cast to the correct type to use the data. For example, to access the contents of a business object array, the return value must be cast to the BusObjContainer type, as shown in this code fragment:

theAttr = theSpec->getAttribute(i);
   if (theAttr->isObjectType()) {
      if(theAttr->isMultipleCard()) {
         // Multiple cardinality object so cast attribute value
         // to a BusObjContainer object
         BusObjContainer *busObjContnr = 
            (BusObjContainer *) theObj.getAttrValue(i);

If the attribute contains a business object array, the doVerbFor() method obtains access to this array through the casted BusObjContainer object that getAttrValue() has returned.

Note:
The deprecated name for an array of business objects is a "business object container". This term is also used to name the connector library class that provides methods for accessing the child business objects in a business object array (BusObjContainer). You can think of this class as providing methods for handling an array of business objects.

To access individual business objects within the business object array, take the following steps:

  1. Call BusObjContainer::getObjectCount() to get the number of child business objects in the array.
  2. As it iterates through the business object array, the verb processing can get each individual child object within the business object array using the BusObjContainer:: getObject(index) method, where index is the array element index. This method returns a pointer to a child business object or NULL if there is no business object at the specified position.

Figure 69 shows the C++ code to access child business objects.

Figure 69. Accessing child business objects in a C++ connector

for (int i=0; i < busObjContnr->getObjectCount(); i++) {
   BusinessObject *currBusObj = busObjContnr->getObject(i);
   status = doVerbMethod(*currBusObj);
}

Figure 70 shows a C++ submethod, doVerbMethod(), that might be called by a main verb method to process child objects. For a business object such as the one shown in Figure 43,, a Create method might first create the application entity for the parent Customer business object, and then call the submethod to traverse the parent business object to find attributes referring to contained business objects.

Figure 70. Processing child business objects in a C++ submethod

int GenBOHandler::doChildCreate(BusinessObject &theObj)
{
   int      i, k;
   int       status = BON_SUCCESS;
 
   for (i = 0; i < theObj.getAttrCount() -1; i++) { 
      theAttr = theSpec->getAttribute(i);
      theAttrVal = theObj.getAttrValue(i);
      if (theAttr->isObjectType()) {
         if(theAttr->isMultipleCard()) {
            // Multiple cardinality object so cast attribute value
            // to a BusObjContainer object
            BusObjContainer *Cont = (BusObjContainer *) theAttrVal;
            if (theAttrVal != NULL) {
               for (k=0; k < Cont->getObjectCount(); k++) {
                  BusinessObject *curObj = Cont->getObject(k);
                  status = doCreate(*curObj);
                  if (status == BON_FAIL) 
                     return status;
               }
            }
         }
         else {
            // Single cardinality object
            if (theAttrVal != NULL) {
               status = doCreate(*(BusinessObject *)theAttrVal);
               if (status == BON_FAIL) 
                  return status;
            }
         }
      }
   }
 
   return status;
}

Copyright IBM Corp. 1997, 2004