As discussed in Business object definitions, a business object definition represents a template for data that can be treated as a collective unit. The purpose of an ODA is to generate business object definitions for objects in a data source. For an ODA to generate business-object-definition content, its ODA class must implement the IGeneratesBoDefs interface.
Table 39 lists the methods that the ODA class must define to
implement the IGeneratesBoDefs interface.
Table 39. Methods in the IGeneratesBoDefs interface
Method | IGeneratesBoDefs method | Description |
---|---|---|
Source-node-generation method | getTreeNodes() | Iteratively performs the following:
|
Content-generation method | generateBoDefs() | Generates the business object definitions for the user-selected source data, writing them to ODA memory |
Content-retrieval method | getBoDefs() | Retrieves either a specified business object definition or all business object definitions from ODA memory |
With the IGeneratesBoDefs interface implemented, Business Object
Wizard invokes the methods shown in Table 40 to obtain source nodes, as well as generate and retrieve
content.
Table 40. Business Object Wizard and IGeneratesBoDefs methods
Step in Business Object Wizard | IGeneratesBoDefs method | For more information |
---|---|---|
Step 3: Select Source | getTreeNodes() | Generating source nodes |
Step 5: Generating Business Objects | generateBoDefs() | Generating business object definitions |
Step 5: Generating Business Objects | getBoDefs() | Providing access to generated business object definitions |
The following sections discuss the implementation of each of the methods in Table 40.
Business Object Wizard calls the getTreeNodes() method to discover the source nodes in the ODA's data source and create the source-node hierarchy, which Business Object Wizard displays in its Select Source dialog box (Step 3). The getTreeNodes() method is part of the IGeneratesBoDefs interface, which the ODA class must implement to support generation of business object definitions.
As Selecting and confirming source data describes, Business Object Wizard uses the tree-node array that getTreeNodes() returns to initialize the Select Source dialog box. This dialog box displays the source-node hierarchy, which allows users to move through the source nodes obtained from the data source and to select those for which the ODA generates business object definitions. Each time a source node is expanded, Business Object Wizard calls the getTreeNodes() method, which returns a tree-node array with the contents of the expanded source node.
For example, the getTreeNodes() method in the sample Roman Army ODA initializes the Select Source dialog box with the top-level army general, which it has obtained from the sample's data source, the RomanArmy.xml file. When a particular node is expanded, getTreeNodes() obtains the sons of that node's army general from the XML file and puts them into a tree-node array. Business Object Wizard uses this tree-node array to display the expanded source node.
Therefore, the purpose of getTreeNodes() is to discover source nodes in the data source, then construct and return an array of tree nodes. To do this, getTreeNodes() performs the following tasks:
When Business Object Wizard calls the getTreeNodes() method, it passes to this method the value of the parent-node path. This path identifies the user-selected node that getTreeNodes() will expand. It is a String that contains the fully qualified path of the node, from the top-level parent down to the user-selected node. Node names within this path are separated with a colon (:).
For example, Figure 61 shows a Select Source dialog box that displays a view of the source-node hierarchy for the sample Roman Army ODA.
Figure 61. Sample source-node hierarchy
In Figure 61, the parent-node path for the Uulius source node is:
Apollo:Tellus:Uulius
Users can specify a parent node to expand by one of the following ways:
Business Object Wizard constructs the parent-node path for the selected source node and passes this path to getTreeNodes().
You must specify the parent-node path with the same syntax that getTreeNodes() expects for its parent-node path. For more information, see Specifying an object path.
The getTreeNode() method uses the parent-node path to determine the level of the source-node hierarchy to return in its tree-node array. This tree-node array will contain all child nodes of the node that the parent-node path identifies. To tell getTreeNodes() to return the top-level of the source-node hierarchy, Business Object Wizard passes in an "empty" parent node path. Therefore, the getTreeNodes() method should check for an empty node path as its first step, as the following code fragment shows:
if (parentNodePath = null || parentNodePath.length() == 0) //return the top-level of the source-node hierarchy
If the parent-node path is not empty, getTreeNodes() should build the tree nodes for the children of the specified parent-node path, returning the appropriate array of TreeNode objects to Business Object Wizard.
Figure 62 shows the implementation of the getTreeNodes() method (defined in the ArmyAgent3 class of the sample Roman Army ODA).
Figure 62. Generating the tree-node array
public TreeNode[] getTreeNodes(String parentNodePath, String searchPattern) throws ODKException { if (parentNodePath == null || parentNodePath.length() == 0) return getNodes(m_army, searchPattern); return getNodes(findSon(parentNodePath, searchPattern)); }
Figure 62 shows an important concept in the implementation of the getTreeNodes() method. This method is often modularized, putting the actual search of the data source into a separate method or even into a separate class. This getTreeNodes() method calls the getNodes() method to actually generate the tree-node array for the selected data-source data. If the parent-node path is empty, getTreeNodes() sends to getNodes() the entire contents of the XML file (in the m_army variable). Otherwise, getTreeNodes() sends to getNodes() the results of the findSon() method, which performs the actual query of the data source.
A search pattern allows you to specify criteria that child nodes must meet to be displayed when the parent node is expanded. You initiate the search-pattern feature by right-clicking and then clicking Search for items. The Enter a Search Pattern dialog box opens, where you can specify the search criteria.
When Business Object Wizard receives a search pattern, it calls getTreeNode() again to retrieve a new tree-node array from the data source. Business Object Wizard passes the search pattern as an argument to getTreeNodes(). The search pattern contains wildcards and other symbols that the underlying data source recognizes. For example, if the data source is a database, valid search criteria could include SQL search symbols such as a percent (%) or question mark (?).
The getTreeNodes() method searches the data source for child nodes that match the search pattern and puts the resulting child nodes in the tree-node array that it returns to Business Object Wizard. In this way, you can dynamically specify new conditions for source nodes to meet.
You should also initialize the searchPatternDesc member variable to a string that describes to the user the valid search criteria. Business Object Wizard displays this string as the text for the Enter the Search Pattern dialog box. You initialize the ODA's metadata in the getMetaData() method. For more information, see Initializing ODA metadata.
For example, if the data source is a database, you can include the search pattern in SQL statements that query the database tables.
In the sample Roman Army ODA, the search pattern allows the user to enter one letter as search criteria. The getTreeNodes() method calls the getNodes() method to handle the actual generation of tree nodes. The following code fragment from this getNodes() method (defined in ArmyAgent3) shows how the method uses the search pattern in its search of the data source:
TreeNode[] getNodes(Son parent, String searchPattern) { Vector nodes = new Vector();
if (searchPattern == null || searchPattern.length() == 0) searchPattern = ""; else searchPattern = new String(new char[] {searchPattern.charAt(0)});
When the getNodes() method later compares the name of the object in the XML file (the data source) with the current name in the parent-node path, it checks if the object name begins with the specified search pattern, as follows:
if (currSon.name.getValue().startsWith(searchPattern))
For the context of this use of the search pattern, see Figure 63.
The main purpose of the getTreeNodes() method is to query the data source to discover source nodes, which are objects for which the ODA can generate content. The mechanism to query the data source depends on the kind of data source with which the ODA works. For example, the XML ODA (a prebuilt ODA that is part of the WebSphere Business Integration Adapters product) queries XML files to present the names of objects within these files for possible content generation. As another example, the JDBC ODA (another prebuilt ODA part of WebSphere Business Integration Adapters) queries a JDBC database to present the names of tables within the database for possible content generation.
As suggested in Table 27, if the logic necessary to query your data source is reasonably complex, you should develop special Java classes to handle this interaction. The getTreeNodes() method can then instantiate and access these classes as needed. Make sure you include these classes in the ODA library file. For more information, see Compiling the ODA.
For the sample Roman Army ODA, the findSon() method (defined in the ArmyAgent3 class) performs the task of querying the data source. It finds a particular soldier (identified by its parent-node path) in the Roman-Army XML file. It returns the information for the specified name as a Son object. The sample defines the Son class to read an object in the XML file.
As the ODA queries the data source for source nodes, it must generate the
associated tree node to represent each source node it discovers. The
ODK API represents a
tree node as an object of the
TreeNode class, which contains the information shown in Table 41.
To create a tree node, use one of the forms of the TreeNode() constructor. For a list of these forms, see TreeNode().
When a node has a "normal" nature, it can have one of the following structures:
Business Object Wizard displays an expandable node with a plus (+) sign to
the left of the node name (to indicate that the user can expand the node) or a
minus
(-) sign (to indicate that the user can contract the node). The
following table shows the initialization that the TreeNode object
requires for it to display as an expandable node:
TreeNode member variable | Value |
---|---|
isExpandable
| true |
nodes
| An array of the child nodes |
isGeneratable
| false (usually) |
For information about how to move through the source-node hierarchy, see Moving through the source-node hierarchy
Business Object Wizard displays a leaf (terminating) node as just the node
name. The following table shows the initialization that the
TreeNode object requires for it to display as a leaf node:
Tree-node member variable | Value |
---|---|
isExpandable
| false |
nodes
| null |
isGeneratable
| true (usually) |
Both the leaf and expandable node are normal-nature nodes. Therefore, they both have the polymorphicNature member variable set to the NODE_NATURE_NORMAL node-nature constant. This constant is defined in the ODKConstant interface (which the TreeNode class implements). The first two forms of the TreeNode() constructor do not specify the polymorphicNature member variable. Therefore, this member variable defaults to NODE_NATURE_NORMAL.
Suppose the ODA generates the source-node hierarchy shown in Figure 61. If the user expands the Uulius node, the getTreeNodes() method must generate a tree-node array that contains the child nodes for Uulius. Because the parent-node path is not empty (it is Apollo:Tellus:Uulius), the getTreeNodes() method makes the following call to the getNodes() method (see Figure 62):
getNodes(findSon(parentNodePath), searchPattern))
This call to getNodes() uses the findSon() method to query the data source for the Uulius node and return a Son object that contains the information from the XML file. One of the member variables in this Son object is a vector of XML objects (XmlObjectVector) with the information on the children of Uulius. Figure 63 shows a code fragment from the getNodes() method that loops through this XML-object vector and creates a TreeNode object for each of the children:
Figure 63. Constructing tree nodes
for (int i=0; i<sons.size(); i++) { Son currSon = (Son) sons.getAt(i); if (currSon.name.getValues().startsWith(searchPattern)) { int age = currSon.age.getIntValue(); int children = currSon.Son == null ? 0 : currSon.Son.size(); int nature = TreeNode.NODE_NATURE_NORMAL;
TreeNode tn = new TreeNode(currSon.name.getValue(), " ", canRecruit(currSon), children > 0, null, nature);
nodes.add(tn); } }
The code fragment in Figure 63 initializes a new TreeNode object for each child soldier node with the soldier's name, whether this node is generatable (based on whether the soldier is of recruitable age), and whether this node is expandable (based on the number of children the soldier has). This call to the TreeNode() constructor does not initialize the tree node with a description (""), nor does it provide any child nodes. Once each new TreeNode object is instantiated, the code adds it to a Java Vector (nodes). When getNodes() has generated TreeNode objects for all child nodes, it copies the contents of this vector into a tree-node array with the following code:
TreeNode[] tn = new TreeNode[nodes.size()]; System.arraycopy(nodes.toArray(), 0, tn, 0, nodes.size());
The getNodes() method returns this tree-node array to getTreeNodes(), which in turn returns this array to its calling program, Business Object Wizard. Business Object Wizard uses this new tree-node array to display the expanded contents of the Uulius node, as Figure 64 shows.
Figure 64. Expanding the Uulius source node
When a node has a "file" nature, the user can associate an operating system file with the node. Business Object Wizard indicates that a node has a file nature by activating the Associate files menu item when the user right-clicks the node name. For information about how to use this menu item, see Associating an operating-system file.
A file-nature node has its
polymorphicNature member variable set to the NODE_NATURE_FILE
node-nature constant. This constant is defined in the
ODKConstant interface (which the TreeNode class
implements). The following table shows the initialization that the
TreeNode object requires for it to function as a file-nature
node:
TreeNode member variable | Value |
---|---|
polymorphicNature | NODE_NATURE_FILE |
isExpandable | false |
nodes | null |
isGeneratable | false (usually) |
In the sample Roman Army ODA, the ArmyAgent4 class implements a getNode() method that supports file-nature nodes. This sample allows the user to associate a file with a source node for any node that represents a soldier that is at least 28 years old (the default minimum age) and has no children of his own. The code for this version of getNode() is almost identical to the code in Figure 63. The only difference is in the assignment of the value to the polymorphicNature member variable. Instead of assigning the NODE_NATURE_NORMAL constant to all nodes, the ArmyAgent4 version of getNode() uses the following code line to set the node nature to NODE_NATURE_FILE if the node represents a soldiers at least 28 years old and having no children:
int nature = m_allowAdoption && canAdopt(currSon) ? TreeNode.NODE_NATURE_FILE : TreeNode.NODE_NATURE_NORMAL;
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).
This section describes the following steps that the generateBoDefs() method should take to generate business object definitions:
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 60). The implementation of this method generateBoDefs() in the ArmyAgent3 class includes the code fragment in Figure 65, which declares the variable for the generated-content structure (m_generatedBOs) and defines the generateBoDefs() method itself.
Figure 65. Defining the generateBoDefs() method
final Vector m_generatedBOs = new Vector();
public ContentMetaData generateBoDefs(String[] nodes) throws ODKException {
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 66 illustrates a sample BO Properties dialog box that displays two business-object properties:
Figure 66. Additional property information needed.
To provide the user with the properties illustrated in Figure 66, the generateBoDefs() method takes the following steps:
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:
To initialize the Verbs and Prefix properties, you provide the AgentProperty() constructor with the following information:
Therefore, this property requires the following metadata in the
AgentProperty() constructor:
Before the call to the AgentProperty() constructor, the code in Figure 67 first creates and initializes the validValues and defaultValues arrays so they are available for the constructor.
Therefore, this property requires the following metadata in the
AgentProperty() constructor:
The code fragment in Figure 67 creates and initializes the business-object-properties array:
Figure 67. 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.
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.
The call to getBOSpecificProps() in Figure 68 sends the AgtProps array (initialized in Figure 67) to Business Object Wizard for display in the BO Properties dialog box.
Figure 68. Displaying the BO Properties dialog box
// Display BO Properties dialog box, initializing it with AgtProps Util.getBOSpecificProps(AgtProps, "For all the Tables selected");
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 68 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");
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:
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 42.
As Table 42 shows, a business object definition contains both metadata and data. The following sections describe how to access these parts of the business object definition:
As Table 42 shows, the metadata of a business object definition consists of the following information:
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()); ...
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.
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.
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.
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 43 summarizes the properties in an attribute object.
These properties correspond to the attribute metadata.
Table 43. Properties of an attribute
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 69 contains a code fragment that creates these attribute objects for the business object definition.
Figure 69. 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 69 takes the following steps:
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.
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().
The code fragment in Figure 69 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:
BusObjAttr attrib = new BusObjAttr(LineItems, OrderLineItems); attrib.setCardinality(BusObjAttr.CARD_MULTIPLE);
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.
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 44 summarizes the metadata in a verb object.
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.
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:
Once Business Object Wizard receives this content-metadata object, it can access the generated business object definitions (within the generated-content structure) as needed with the getBoDefs() method.
The code sample in Figure 70 shows the last part of the generateBoDefs() method for the sample Roman Army ODA.
Figure 70. 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 70 handles the generated content as follows:
As shown in Figure 65, the Roman Army sample uses a Java vector called m_generatedBOs as its generated-content structure. This structure is global to the methods of the Roman Army ODA class. To save the business object definition it has generated, generateBoDefs() saves it in the m_generatedBOs vector. This step is within a loop that terminates when the ODA has generated business object definitions for all source nodes that users selected. When Business Object Wizard needs to access the generated-content structure, it calls the content-retrieval method, getBoDefs().
The generateBoDefs() method instantiates a
ContentMetaData object and into this constructor passes the
information shown in Table 45.
Table 45. Initializing the content metadata for business-object-definition generation
ContentMetaData information | Code | Description |
---|---|---|
Content type | ContentType.BusinessObject |
Indicates that the content type is business object definitions
|
Size of the generated content | -1 |
Indicates that total size is not required. The length
value is not needed in the current implementation of a
ContentMetaData object.
|
Count of the generated content | m_generatedBOs.size() |
The size() method returns the number of elements currently in
the vector.
|
The generateBoDefs() method does not return the actual generated business object definitions. For Business Object Wizard to be able to access the generated content, the ODA class must implement the content-retrieval method for business object definitions. Business Object Wizard uses the information in the content-metadata object (which generateBoDefs() does return) to determine whether to call the appropriate content-retrieval method. If generateBoDefs() has successfully generated business objects, Business Object Wizard calls the getBoDefs() method to retrieve the generated business object definitions.
To provide access to generated business object definitions, the ODA class
must implement the getBoDefs() method. This method is
defined as part of the
IGeneratesBoDefs interface. The method accepts as an argument an
index, which identifies the number of business object definitions to
return. It access these business object definitions in the
generated-content structure and returns an array of the retrieved
business-object-definition (BusObjDef) objects. The number
of business object definitions in this array depends on the value of the index
argument, as Table 46 shows.
Table 46. Retrieving business object definitions
For the sample Roman Army ODA, the generateBoDefs() method (defined in the ArmyAgent3 class) populates the m_generatedBOs vector with its generated business object definitions. Therefore, the getBoDefs() method (also defined in ArmyAgent3) retrieves the specified number of business object definitions from this vector and copies them into its return array. The following code shows the getBoDefs() method for the sample Roman Army ODA:
public BusObjDef[] getBoDefs(long index) throws ODKException { BusObjDef[] bos = null;
if (index == ODKConstant.GET_ALL_OBJECTS) { bos = new BusObjDef[m_generatedBOs.size()] System.arraycopy(m_generatedBOs.toArray(), 0, bos, 0, m_generatedBOs.size()); }
else bos = new BusObjDef[] {(BusObjDef)m_generatedBOs.get((int)index)};
return bos; }