Creating a business object handler involves the following steps:
In the C++ connector library, the base class for a business object handler the BOHandlerCPP class. The BOHandlerCPP class provides methods for defining and accessing a business object handler.
To extend a business-object-handler base class for a C++ business object handler, follow these steps:
connectorNameBOHandler.cpp
For example, to create a business object handler for a Baan application, you 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.
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".
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.
The role of the business object handler is to perform the following tasks:
Table 54 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 54. 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: |
|
|
| "Performing the verb operation" |
|
| "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 54, this section also provides additional processing information in "Additional processing issues".
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 55 lists the method that the C++ connector library provides to
retrieve the active verb from the request business object.
Table 55. 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:
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.
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.
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 56.
Table 56. 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 ... }
When the init() method in the connector base 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 57 to notify the connector framework of the lost
connection.
Table 57. 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; |
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 ... }
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 meta-data-driven method:
For verb-processing that is not meta-data-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.
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.
For meta-data-driven verb-processing method, the application-specific
information for the verb contains meta-data, which provides processing
instructions for the request business object when that particular verb is
active. Table 58 lists the method that the C++ connector library provides to
obtain application-specific information for the verb of a business
object.
Table 58. 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, meta-data-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.
The method retrieves the attribute values from the business object instance, and fills in the form using the attribute meta-data to identify the fields of the form for each attribute. The method identifies the verb operation in the business object, retrieves the verb meta-data 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. Meta-data-driven verb processing
int ExampleBOHandler::doVerbFor(BusinessObject &theObj, ReturnStatusDescriptor *rtnObj) { BusObjSpec *theSpec; int status = BON_SUCCESS; // Get the business object definition and its meta-data: // 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 meta-data: 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; }
Table 59 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 59. Performing the verb operation
Verb | Use of request business object | For more information |
---|---|---|
Create |
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 |
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 |
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 |
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" |
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.
Table 60 summarizes the steps in the basic program logic for
deconstructing a request business object that contains meta-data.
Table 60. Basic logic for processing a request business object with meta-data
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 60 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".
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 61 lists the method that the C++ connector library provides to
obtain the business object definition for the current business object (a
BusinessObject instance).
Table 61. 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()
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 62. For a complete list of BusObjSpec
methods, see BusObjSpec class.
Table 62. 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()
|
|
getAttributeIndex()
|
|
getAttribute()
|
Application-specific information: |
|
|
getAppText()
|
|
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.
Business objects for meta-data-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 63 lists the method that the C++ connector library provides to
retrieve application-specific information from the business object
definition.
Table 63. Method for obtaining business object application-specific information
C++ connector library class | Method |
---|---|
BusObjSpec | getAppText() |
As Table 63 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 37). 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); ... }
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 64 lists the methods that the C++ connector library provides to
obtain the attribute descriptors from the current business object.
Table 64. 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:
theAttr = theSpec->getAttribute(attrName);
The following call to getAttribute() returns a pointer to the BOAttrType instance that represents the attribute at the specified ordinal position of the business object definition, indicated by the variable i:
for (i = 0; i < theSpec->getAttributeCount(), i++) { theAttr = theSpec->getAttribute(i); // do processing on the attribute descriptor }
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 65 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
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()
|
If business objects for meta-data-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 66 lists the method that the C++ connector library provides to retrieve application-specific information from an attribute descriptor.
Table 66. Method for obtaining attribute application-specific information
C++ connector library class | Method |
---|---|
BOAttrType | getAppText() |
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 37).
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:
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.
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:
In this example, the destination application uses the same key value that was generated by the source application. This key value is simply passed to the destination application in the business object. If the destination application generates its own keys, the business object typically does not contain values for keys, and the key attribute might be set to the special Ignore value.
If the Create verb method processes the first attribute, which contains the key, the loop index variable starts at 0. However, if your application generates keys, your Create verb method will not process attributes containing keys. In this case, the loop index variable starts at a value other than 0.
The getAttrCount() method returns the total number of attributes in the business object definition. However, this total includes the ObjectEventId attribute. Because the ObjectEventId attribute is used by the IBM WebSphere business integration system and is not present in application tables, a verb method does not need to process this attribute. Therefore, when looping through business object attributes, you loop from zero to one less than the total number of attributes:
getAttrCount() - 1
This increment of the index obtains the next attribute descriptor when getAttribute(i) is called.
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 67 lists some of the methods that the C++ connector library provides to determine whether an attribute should be processed.
Table 67. 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 67, a verb operation can determine that an attribute is one that the operation wants to process:
The BOAttrType::isObjectType() method checks that the attribute value does not represent a contained business object. For more information on how to handle an attribute that does contain a business object, see "Accessing child business objects"
You can use the getAppText() method to determine if the attribute in the business object definition has application-specific information. Because neither of these special types of attributes represent columns in an application entity, there is no need for the business object definition to include application-specific information for them.
The verb operation can compare the attribute's value to the Ignore and Blank values with the isIgnoreValue() and isBlankValue() methods, respectively. For more information on the Ignore and Blank values, see "Handling the Blank and Ignore values".
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.
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 68 lists the methods that the C++ connector library provides to
obtain attribute values from a business object.
Table 68. 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 68 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.
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.
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; } } }
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.
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
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 69 lists the methods that the C++ connector library provides to
save attribute values in a business object.
Table 69. 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 69 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.
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".
The doVerbFor() method provides an integer outcome status as its return code. As Table 70 shows, the C++ connector library provides constants for the outcome-status values that doVerbFor() is mostly likely to return.
Table 70. Outcome-status values for a C++ doVerbFor()
The outcome status that doVerbFor() returns depends on the
particular active verb it is processing. Table 71 lists the tables in this manual that provide possible return
values for the different verbs.
Table 71. 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 36 |
Using the outcome status that doVerbFor() returns, the connector framework determines its next action:
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 72 lists the status information that this structure
provides.
Table 72. 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.
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:
If your doVerbFor() method returns one of these outcome-status values, make sure it updates the request business object with response information.
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 meta-data 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; }
This section provides the following additional information about how to process the request business object:
In addition to a regular attribute value, simple
attributes in business objects can have either of the special
values shown in Table 73.
Table 73. 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 |
---|
|
The connector can call C++ connector library methods to determine whether a business object attribute is set to a special value:
Table 74. Methods for determining if an attribute contains the Blank value
When an attribute contains the Blank value, the doVerbFor() method should process the attributes as Table 76 shows.
Table 75. Methods for determining if an attribute contains the Ignore value
When attributes are set to the Ignore value, the connector should process
the attributes shown in Table 77.
Table 76. Processing actions for the Blank Value
Table 77. Processing actions for the Ignore 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)); } }
As discussed in "Processing hierarchical business objects", a C++ connector uses the methods of the C++ connector library
shown in Table 78 to access a child object.
Table 78. 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.
To access individual business objects within the business object array, take the following steps:
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; }