Generating business object definitions

After users have selected the source nodes in the Select Nodes dialog box (Step 3), the ODA is ready to begin content generation. Business Object Wizard calls the generateBoDefs() content-generation method to generate business object definitions for the user-selected source nodes. To the ODA, Business Object Wizard sends the list of source nodes (selected in Step 3). The goal of the business-object-definition generation process is to create a business object definition for each selected source node. While the generateBoDefs() method runs, Business Object Wizard displays its Generating Business Objects screen (Step 5).

Note:
Because the ODA generates business object definitions "on request", Business Object Wizard explicitly calls the generateBoDefs() method to initiate generation of the business object definitions. Therefore, you must implement generateBoDefs() so that it handles generating the business object definitions (BusObjDef objects), storing them in the generated-content structure, and returning of content metadata to Business Object Wizard.

This section describes the following steps that the generateBoDefs() method should take to generate business object definitions:

  1. Defining the generateBoDefs() method
  2. Requesting business-object properties
  3. Creating the business object definitions
  4. Providing generated business object definitions

Defining the generateBoDefs() method

To provide generation of business object definitions, your ODA class (derived from ODKAgentBase2) must implement the generateBoDefs() method, is defined in the IGeneratesBoDefs interface. The generateBoDefs() method receives these user-selected source nodes as an argument, an array of source-node paths (String objects). The method must generate a business object definition for each source node in this array. It can use its path to locate the source node in the data source. As its last step, generateBoDefs() returns a content-metadata (ContentMetaData) object to describe the business object definitions it has generated.

The sample Roman Army ODA supports the on-request content protocol for the generation of business object definitions (see Figure 58). The implementation of this method generateBoDefs() in the ArmyAgent3 class includes the code fragment in Figure 63, which declares the variable for the generated-content structure (m_generatedBOs) and defines the generateBoDefs() method itself.

Figure 63. Defining the generateBoDefs() method

final Vector m_generatedBOs = new Vector();
 
public ContentMetaData generateBoDefs(String[] nodes)
     throws ODKException
 {
 

Requesting business-object properties

If, during the content-generation process, the ODA requires additional information, it can display the BO Properties dialog box and request values for business-object properties. For an introduction to business-object properties, see Obtaining business-object properties.

Figure 64 illustrates a sample BO Properties dialog box that displays two business-object properties:

Figure 64. Additional property information needed.

To provide the user with the properties illustrated in Figure 64, the generateBoDefs() method takes the following steps:

  1. Creates the business-property array for the Verbs and Prefix properties.
  2. Calls the getBOSpecificProps() method to display the business-object properties.
  3. Obtains user-initialized values for the business-object properties.
Creating the business-property array

The getBOSpecificProps() method requires an array of agent-property objects as an argument. This argument is the business-object-property array and contains one agent-property object for each business-object property to display in the BO Properties dialog box. Before generateBoDefs() calls getBOSpecificProps(), it must take the necessary steps to create the array that defines the business-object properties, initialize the business-object properties, and save these properties into the array.

The first step is to define a business-object-property array to hold the Verbs and Prefix properties. The next step is to initialize the business-object properties, using the AgentProperty() constructor. With this constructor, you specify values for the various metadata that the AgentProperty class supports. The AgentProperty class provides support for the business-object property to have the following features:

Note:
For more information, see Working with agent properties.

To initialize the Verbs and Prefix properties, you provide the AgentProperty() constructor with the following information:

The code fragment in Figure 65 creates and initializes the business-object-properties array:

Figure 65. Creating the business-object array

// Create the business-object-property array
    AgentProperty AgtProps[] = new AgentProperty[2];
 
  // Provide list of valid values for Verbs property
    Object[] validValues = new Object[4];
    validValues[0] = new String("Create");
    validValues[1] = new String("Retrieve");
    validValues[2] = new String("Delete");
    validValues[3] = new String("Update");
 
 // Provide list of default values for Verbs property
    Object[] defaultValues = new Object[4];
    defaultValues[0] = new String("Create");
    defaultValues[1] = new String("Retrieve");
    defaultValues[2] = new String("Delete");
    defaultValues[3] = new String("Update");
 
// Instantiate the Verbs property
    AgtProps[0] = new AgentProperty("Verbs", AgentProperty.TYPE_STRING, 
          "Verbs that are applicable to all the selected objects",
          false, true, ODKConstant.MULTIPLE_CARD, validValues,
          defaultValues);
 
// Instantiate the Prefix property
    AgtProps[1] = new AgentProperty("Prefix", AgentProperty.TYPE_STRING,
          "Prefix that should be applied to each business object name",
          false, false, ODKConstant.SINGLE_CARD, null, null);
 

For more information on the metadata of business-object properties, see Working with agent properties.

Displaying the BO Properties dialog box

Once the business-object-property array is initialized, the ODA can call the getBOSpecificProps() method to pass this array to Business Object Wizard for display to the user in the BO Properties dialog box. This method is defined in the ODKUtility class, and therefore must access an ODKUtility object. Usually, you instantiate this object as part of the ODA initialization. For more information, see Obtaining the handle to the ODKUtility object.

Note:
If any property values are invalid, getBOSpecificProps() throws the ODKInvalidPropException exception.

The call to getBOSpecificProps() in Figure 66 sends the AgtProps array (initialized in Figure 65) to Business Object Wizard for display in the BO Properties dialog box.

Figure 66. Displaying the BO Properties dialog box

// Display BO Properties dialog box, initializing it with AgtProps
    Util.getBOSpecificProps(AgtProps, "For all the Tables selected");
 
Retrieving the user-specified values

Once users have specified values for the business-object properties in the BO Properties dialog box and clicked Next, Business Object Wizard sends the user-specified values back to the ODA. The ODA can retrieve these values in either of the following ways:

The getBOSpecificProps() call in Figure 66 did not save the Hashtable object that Business Object Wizard creates. Therefore, this code fragment uses the getBOSpecificProperty() method to get the value of the properties specified for the verbs and each business object prefix:

// Get the value of the Verbs and the Prefix properties
 AgentProperty propVerb =
       Util.getBOSpecificProperty("Verbs");
 AgentProperty propPrefix =
       Util.getBOSpecificProperty("Prefix");
 

Creating the business object definitions

The generateBoDefs() method must generate a business object definition for each source node in the array it receives as an argument from Business Object Wizard. To generate a business object definition, generateBoDefs() takes the following steps:

  1. Use the source node's path, from the array that generateBoDefs() receives from Business Object Wizard, to locate the associated object in the data source.
  2. Obtain any information needed to populate the business object definition from the associated object in the data source.
  3. Create a business-object-definition object to represent the source node.
  4. Populate this business-object-definition object with the information obtained from the associated object in the data source (step 2).

The ODK API represents a business object definition as a business-object-definition (BusObjDef) object. You can use the BusObjDef() constructor to instantiate the new business object definition and provide it with a name. You can then provide the business object definition with the information shown in Table 40.

Table 40.

Contents of a business object definition
Business-object-definition information Description Accessor method
metadata


Name The name of the business object definition getName()

Application-specific information The business-object-level application-specific information, which contains information applicable to the entire business object definition getAppInfo(), setAppInfo()
Data


Attribute list A list of the attributes in the business object definition; each attribute is a BusObjAttr object. getAttributeList(), setAttributeList(), insertAttribute(), removeAttribute()

Verb list A list of supported verbs in the business object definition; each verb is a BusObjVerb object. getVerbList(), setVerbList(), insertVerb(), removeVerb()

As Table 40 shows, a business object definition contains both metadata and data. The following sections describe how to access these parts of the business object definition:

Defining the metadata for the business object definition

As Table 40 shows, the metadata of a business object definition consists of the following information:

Naming the business object definition

The generateBoDefs() method receives the list of user-selected source nodes as an argument. This list is an array of String objects that contains the node paths for the user-selected source nodes. (For information on node paths, see Determining the parent-node path.) With this array, the ODA must create the appropriate name for the business object definition associated with each source node. Usually, the assumption that the ODA makes is that the name of the business object definition matches (or is based on) the name of the data-source object that the source node represents. The ODA must parse the source-node path to obtain the name of the source node, use this source-node name to locate the associated data-source object, then obtain the name from the data-source object.

For example, in the Roman Army sample, the names of the data-source objects and business object definitions match. Therefore, the sample code calls the findSon() method (defined in the ArmyAgent3 and ArmyAgent4 classes) to obtain the data-source object that the source node represents using the source node's node path from the input array of source nodes (nodes), as follows:

for (int i=0; i<nodes.length; i++)
    {
    Son sonNode = findSon(nodes[i]);
    BusObjDef sonBo = new BusObjDef(sonNode.name.getValue());
    ...
 
Note:
All forms of the BusObjDef() constructor specify the name of the business object definition.

The findSon() method parses the source-node path to obtain the name of the last node in the path.

As another example, suppose the data source is a database and its source nodes represent tables. If the source-node paths include the schema names (schema:table), your ODA needs to parse the source-node paths to assign just a table name to the corresponding business object definitions. If your ODA supports a user-specified prefix for business object definitions (with a configuration variable), the ODA must prepend this prefix before it calls BusObjDef() constructor to create the business-object-definition object, as the following code fragment shows:

AgentProperty propPrefix = getBOSpecificProperty("Prefix");
 for (int i=0; i<names.length; i++)
    {
    strToken = new StringTokenizer(names[i], ":");
    schemaName = strToken.nextToken();
    tableName = strToken.nextToken()
 
   if (propPrefix.allValues != null && propPrefix.allValues[0] != null)
       boDef = new BusObjDef(propPrefix.allValues[0] + tableName);
    else
       boDef = new BusObjDef(tableName);
    ...
 

If your data-source objects do not have the exact names you want to assign to your business object definitions, the ODA must parse or in some way format these names as needed.

Generating business-object application-specific information

As Business object application-specific information describes, application-specific information is a powerful way to put application-specific processing information within the business object definition. By moving this information from the processing program (such as a connector), the processing program can be metadata-driven; that is, it can be written in a more generic fashion and obtain its application-specific processing instructions from the business object definition. Therefore, if your business object definitions are to be used with metadata-driven processing programs, it is important that they include the correctly formatted application-specific information at the business-object, attribute, and verb levels.

Note:
For information on attribute application-specific information, see Generating attributes. For information on verb application-specific information, see Supplying supported verbs.

The business object definitions that the Roman Army sample generates do not provide application-specific information. However, suppose the data source was a database with tables as its source nodes. The ODA would generate business object definitions for each user-selected table. In this business object definitions, you might include the name of the table as business-object-level application-specific information. The following code fragment uses the setAppInfo() method, defined in the BusObjDef class, to create the appropriate name-value pairs for this business-object-level application-specific information:

boDef.setAppInfo("TN=" + tableName + ";SCN=" + schemaName +";");
 

This code creates the TN and SCH name-value pairs to represent the table and schema names, respectively. It concatenates the table name and schema name with the tag used to name the element. It then uses the setAppInfo() method to assign this entire string as the business-object-level application-specific information.

Generating attributes

A business object definition contains attributes, which describe the object that the business object definition represents. The business object definition holds the attributes in its attribute list. The ODK API represents an attribute as an attribute (BusObjAttr) object. To instantiate an attribute object, use the BusObjAttr() constructor.

Table 41 summarizes the properties in an attribute object. These properties correspond to the attribute metadata.

Table 41. Properties of an attribute

Attribute property Description Accessor method
Name The name of the attribute getName(), setName()
Application-specific information The attribute-level application-specific information, which contains information applicable to the attribute getAppText(), setAppText()
Type The data type of the attribute's value getAttrType(), getAttrTypeName(), setAttrType()
Cardinality The cardinality of the attribute, which identifies the number of values the attribute holds getCardinality(), setCardinality()
Default value The value to assign to the attribute before the user enters a value getDefault(), setDefault()
Maxlength The maximum length of the attribute's value getMaxLength(), setMaxLength()
Comments Optional comments to describe the purpose of the attribute getComments(), setComments()
Relationship type A string to identify the type of relationship in which the attribute participates getRelationType(), setRelationType()
Primary key Whether the attribute is part of a primary key isKey(), setIsKey()
Foreign key Whether the attribute is part of a foreign key isForeignKey(), setIsForeignKey()
Required key Whether the attribute is required isRequiredKey(), setIsRequiredKey()
Important

The business-object-definition generation process automatically creates the

ObjectEventId attribute. If Business Object Wizard saves the business object definition to a file, it automatically adds the repository version to the top of this file. The repository version is necessary if the integration broker is InterChange Server.

In the sample Roman Army ODA, each business object definition represents a Roman soldier. The generatesBoDefs() method creates the following attributes for business object definition:

Figure 67 contains a code fragment that creates these attribute objects for the business object definition.

Figure 67. Generating attributes

// 1. Create an attribute object for Age attribute
    BusObjAttr attr = new BusObjAttr("Age", BusObjAttrType.INTEGER,
       BusObjAttrType.AttrTypes[BusObjAttrType.INTEGER]);
 
//    Set the Age attribute as the business object definition's key
    attr.setIsKey(true);
 
//    Add the attribute to the business object definition's attribute list
    sonBo.insertAttribute(attr);
 
// 2. Create an attribute object for ChildNo attribute
    attr = new BusObjAttr("ChildNo", BusObjAttrType.INTEGER,
       BusObjAttrType.AttrTypes[BusObjAttrType.INTEGER]);
 
//    Set the default value to number of children
    attr.setDefault(sonNode.Son == null ? "0" : "" + sonNode.Son.size());
 
//    Add the attribute to the business object definition's attribute list
    boDef.insertAttribute(attr);
 

To create the Age attribute, the code fragment in Figure 67 takes the following steps:

  1. Use the BusObjAttr() constructor to create the Age attribute object (attr). It uses the form of this constructor that initializes the attribute object with its name, type, and type name.

    To initialize the type, the code specifies the attribute-type constant for Integer ( BusObjAttrType.INTEGER). To initialize the type name, it uses the AttrTypes member variable in the BusObjAttrType interface. This static member variable provides the type names for all supported attribute types and can be indexed by the attribute-type constants. In this way, you can assign the type name without hardcoding the type-name string.

  2. Use the setIsKey() method to explicitly set the primary-key property to true.

    Because this form of the BusObjAttr() constructor specifies only three attribute properties, all other attribute properties default to "undefined". Therefore, after the BusObjAttr() call, the primary-key attribute property is false. To indicate that the Age attribute is the key attribute, the code sample calls setIsKey().

  3. Use the insertAttribute(), defined in the BusObjDef class, to add the Age attribute to the business object definition's attribute list.

The code fragment in Figure 67 repeats these basic steps to generate the ChildNo attribute. The main difference is that because ChildNo is not the key attribute, no call to setIsKey() is needed. However, the code fragment does provide a default value for this attribute by calling the setDefault() method.

The business object definitions that the Roman Army sample generates are very simple. Only two attributes exist in the business object definition and their names are known at compile time. In addition, only a few attribute properties must be set. For a more complex example, suppose the data source was a database, with tables as its source nodes and as the names of its business object definitions. In this case, the database columns would correspond to the attributes of the business object definition. Many more of the attribute properties would need to be set for these attributes.

The following code fragment creates attributes for the columns in a database table:

Vector Attributes;
 
// 1. Retrieve columns from database table into 'rst' result set
 try{
    ResultSet rst = null;
 
   // Retrieve columns from database
    rst = db.dbmd.getColumns(null, schemaName, tableName, "%");
  
    String colName = null;
    String colType = null;
    int cType = 0;
    int colSize = 0;
 
   // Obtain next column from result set
    rst.next();
    do{
       // Get column name & type
       colName = rst.getString(4);
       colType = rst.getString("DATA_TYPE");
  
       // Convert database types to supported types.
       // Load converted types into the cType variable
       // (steps not shown)
 
// 2. Create an attribute object for each column in the result set.
       Attributes = new Vector(1, 10);
 
      try
         {
          // Create attribute object for column
          BusObjAttr attrib = new BusObjAttr(colName, cType);
 
         // Set the cardinality and maxLength attribute properties
          attrib.setCardinality(BusObjAttr.CARD_SINGLE);
          colSize = rst.getInt("COLUMN_SIZE");
          attrib.setMaxLength(colSize);
 
         // Determine whether it is a primary key in the table: compare
          // column name against earlier retrieve of table's primary keys
          // (stored in pKeys -- code not included here)
          if (pKeys.contains(colName))== true {
             attrib.setIsKey(true);
          }else
             attrib.setIsKey(false);
 
         // Determine whether it is a foreign key in the table: compare
          // column name against earlier retrieve of table's primary keys
          // (stored in fKeys -- code not included here)
          if (fKeys.contains(colName))== true {
             attrib.setIsForeignKey(true);
          }else
             attrib.setIsForeignKey(false);
 
         // Set the isRequired property
          if ((rst.getString("IS_NULLABLE").equals("NO")) &&
                (attrib.isKey() != true)){
             attrib.setIsRequiredKey(true);
          }
 
        // Create attribute application-specific information: 
         // CN tag provides column name
          String asi = "CN="+colName;
          attrib.setAppText(asi);
          attrib.setDefault("");
 
        // Add attribute object to Attributes vector
          Attributes.add(attrib);
          ...
 
// 3. Save the attribute vector as the business object definition's attribute list 
         boDef.setAttributeList(Attributes);
 

The steps in this process are as follows:

  1. Use the BusObjAttr() constructor to create a simple business object attribute from the column information. This form of the constructor specifies only the attribute name and type.
  2. Set the cardinality and maxLength attribute properties, based on these values from the column in the database.
    Note:
    To create an attribute that represents a child business object or an array of child business objects, specify the name of the child business object as the type, and set the cardinality to 1 or n, as appropriate. For example, to create an attribute named LineItems that represents an array of OrderLineItems business objects, use the following code:
    BusObjAttr attrib = new BusObjAttr(LineItems, OrderLineItems);
     attrib.setCardinality(BusObjAttr.CARD_MULTIPLE);
     
    
  3. Get primary-key and foreign-key information to set any attributes that represent primary or foreign keys. The code fragment compares the current column name with names in existing arrays that contain primary-key columns (pKey) and foreign-key columns (fKey) selected from the database. Code that selects the primary- and foreign-key columns is not shown here.
  4. Set the "is required key" attribute property, based on whether the attribute is the primary key.
  5. Set the attribute-level application-specific information.

    For business object definitions generated for database tables, you might include the name of the column as attribute-level application-specific information for each attribute in the business object definition. This code fragment uses the setAppText() method, defined in the BusObjAttr class, to create the CN name-value pair for the attribute-level application-specific information. The code concatenates the column name with the CN tag. It then uses the setAppText() method to assign this entire string as the attribute's application-specific information.

  6. Use the setAttributeList() method, defined in the BusObjDef class, to assign the generated attributes vector (Attributes) as the attribute list of the business object definition.
Supplying supported verbs

A business object definition contains supported verbs, which describe the operations that can be performed on business objects of that business object definition. The business object definition holds its supported verbs in its verb list. The ODK API represents a verb as a business-object-verb (BusObjVerb) object. To instantiate a verb object, use the BusObjVerb() constructor.

Table 42 summarizes the metadata in a verb object.

Table 42. metadata for a verb

Verb metadata Description Accessor method
Name The name of the supported verb (such as Create, Retrieve, Update, or Delete) getName(), setName()
Application-specific information The verb-level application-specific information, which contains information applicable only to the verb getAppInfo(), setAppInfo()

In the Roman Army sample, the generatesBoDefs() method assigns to each business object definition one supported verb of Create. The following code fragment uses the insertVerb() method, defined in the BusObjDef class, to add the Create verb to the business object definition's verb list:

sonBo.insertVerb("Create", null);
 

The business object definitions that the Roman Army sample generates do not provide application-specific information. Therefore, the second argument to this insertVerb() call, which provides verb application-specific information, is null.

The ODA can use the BO Properties dialog box to obtain verb support for the generated business object definitions. By defining a business-object property called Verbs and allowing users to select the supported verbs, the ODA can obtain more customized verb support. For more information on the use of the BO Properties dialog box, see Requesting business-object properties.

The following code fragment assumes that the ODA has obtained user-specified values for a business-object property called Verbs and uses this property to obtain the verbs to the business object definition's verb list.

Vector Verbs;
 AgentProperty propVerbs = getBOSpecificProperty("Verbs");
  
 if (propVerbs.allValues[0] != null)
    {
    int len = propVerbs.allValues.length;
    BusObjVerb verb;
  
    for(int i=0; i<len; i++)
       {
       if(propVerbs.allValues[i] != null) 
          {
          try {
             verb = new BusObjVerb(propVerbs.allValues[i].toString(), "");
             Verbs.add(verb);
          }
       }
    }
 ...
 boDef.setVerbList(Verbs);
 

This code fragment uses the BusObjVerb() constructor to copy a verb into the verb variable of type BusObjVerb. It then loads a String version of that verb object into the Verbs vector. The code does not specify verb application-specific information. Finally, the code fragment uses the setVerbList() method, defined in the BusObjDef class, to assign the generated verbs vector (Verbs) as the verb list of the business object definition.

Providing generated business object definitions

As discussed in Providing generated content, the ODA must return the generated content to Business Object Wizard in two parts. Therefore, if the ODA generates business object definitions as content, it must return the following:

Because the ODA must generate business object definitions "on request", the generateBoDefs() method provides this content information, as follows:

Note:
For more information on getBoDefs(), see Providing access to generated business object definitions.

The code sample in Figure 68 shows the last part of the generateBoDefs() method for the sample Roman Army ODA.

Figure 68. Providing generated business object definitions

           m_generatedBOs.add(sonBo);
       } // this for loop terminates when all bus obj defs are generated
 
      return new ContentMetaData(ContentType.BusinessObject, -1,
          m_generatedBOs.size());
 } // end of generateBoDefs() 
 

Figure 68 handles the generated content as follows:

Copyright IBM Corp. 1997, 2003