Implementing the methods

To develop a data handler, you implement the following methods of the DataHandler class:

The methods of your custom data handler go in the Java source file of the DataHandler class that you created in Extending the data handler base class.

Note:
If the caller re-uses a cached instance of the DataHandler class over multiple threads, you might need to make the class thread-safe. To determine whether this is required, see the Connector Development Guide for Java for more details on the threading model.

Implementing the abstract methods

The data-handler base class, DataHandler, provides the abstract methods in Table 75, which you must implement in the DataHandler class for your custom data handler.

Table 75. Abstract methods in the DataHandler class

Data conversion Format of serialized data DataHandler method
String-to-business-object conversion Converts serialized data, accessed through a Reader object, to a business object getBO() - abstract
Business-object-to-string conversion Converts a business object to an InputStream object getStreamFromBO()

Converts a business object to a String object getStringFromBO()

Converts a business object to a byte array. getByteArrayFromBO()
Note:
The copy of the StubDataHandler.java file that extends the DataHandler class with your custom data handler contains declarations for the abstract methods you need to implement.

The following sections provide implementation information for each of the abstract DataHandler methods.

Implementing conversion to a business object

The abstract getBO() method performs the string-to-business-object conversion; that is, it populates a business object with data extracted from a Java Reader object. There are two versions of the getBO() method:

Note:
To support the data handler when it is called in the context of a connector, your data-handler class (which extends DataHandler) must implement both versions of the getBO() method. To support the data handler when it is called only from an access client (IBM WebSphere InterChange Server integration broker only), you must implement only the second form of the getBO() method; the Server Access Interface uses only this second form of getBO().

The getBO() method allows the caller to pass in an optional object containing configuration information (the config parameter). This information is in addition to the configuration information specified in the data-handler meta-object. As an example, the configuration object can point to a template file or to a URL that the data handler uses.

Note:
When converting to a business object, getBO() must ensure that any attribute identified with a cw_mo_label tag in the application-specific information of the parent business object does not get a value. For more information on the cw_mo_label tag, see "Implementing conversion from a business object".

The purpose of the abstract getBO() method is to populate a business object with the serialized data that the Reader object contains. The public versions of getBO() can then receive the serialized data in one of several supported forms, convert the data to a Reader object, and then call the abstract getBO() method to perform the actual string-to-business-object conversion. For more information on the public getBO() method, see getBO() - public.

Figure 41 shows a basic implementation of the second form of the getBO() method. The example illustrates the steps in the conversion of data from a Reader object that contains fixed-width data to a business object:

  1. The getBO() method converts the data in the Reader object to a String object. It then calls the user-defined getBOFromString() method to create an instance of the business object.
  2. The getBOFromString() method determines what type of business object to create based on the first fixed-width token in the String, and then it creates a business object instance of that type. It gets the verb from the second fixed-width token in the String and sets the verb in the business object. This method then calls the user-defined parseAttributeList() method to parse the remainder of the String and populate the business object with values.
  3. The parseAttributeList() method parses the String and recursively populates the business object. When the method finds an attribute of an object type, it determines whether the object is single or multiple cardinality. It calls getMultipleCard() to recursively process the business objects in the array and getSingleCard() to process a single-cardinality child business object.
Tip:
A data handler extracts data from business objects and populates business objects with data in the same way that a connector does. For example, in the following code sample, the getBOFromString() method calls JavaConnectorUtil.createBusinessObject() to create a business object instance and BusinessObjectInterface.setVerb()to set the verb. For information on how to process business objects, see the Connector Development Guide for Java.

Figure 41. Example getBO() method

public BusinessObjectInterface getBO(Reader serializedData,
     Object config)
     throws Exception
 {
     clear(config);
     BusinessObjectInterface resultObj = null;
  
     // Create a String object from the Reader, then use the string
     // method
     int conversionCheck;
     char[] temp = new char[2000];
     StringBuffer tempStringBuffer = new StringBuffer(1000);
  
     while ( (conversionCheck = serializedData.read(temp)) != -1 )
         tempStringBuffer.append(new String (temp, 0, conversionCheck));
  
     mBOString = new String(tempStringBuffer);
     mBOStringLength = mBOString.length();
  
     resultObj = getBOFromString(null);
     return resultObj;
 }
  
  
 // Gets business object name and verb and creates a bus obj instance
 private BusinessObjectInterface getBOFromString(String pvBOType)
     throws Exception
 {
     BusinessObjectInterface returnObj = null; 
     String lvBOName = null;
     String lvVerb = null;
  
     lvBOName = this.getNextToken(mBONameSize, true);
     lvVerb = this.getNextToken(mBOVerbSize, true);
  
     if( (pvBOType != null) && (lvBOName.compareTo(pvBOType) != 0) ) {
         throw new Exception(...);
     }
     else
     {
         returnObj = JavaConnectorUtil.createBusinessObject(lvBOName);
     }
  
     returnObj.setVerb(lvVerb);
  
     parseAttributeList(returnObj);
  
     return returnObj;
 }
 
// Parse String to populate the attributes in the business object
 protected void parseAttributeList(BusinessObjectInterface pvBO)
     throws Exception
 {
     if ( mEndOfBOString )
         throw new Exception(...);
     else if( pvBO == null )
         throw new Exception(...);
  
     int lvAttrNum = pvBO.getAttrCount();
             
     String lvAttrName = null;
     String lvAttrValue = null;
     int lvAttrMaxLength = 0;
  
     try {
         for (int lvAttrIndex = 0; lvAttrIndex < lvAttrNum;
                 lvAttrIndex++)
         {
             CxObjectAttr lvAttrSpec = pvBO.getAttrDesc(lvAttrIndex);
             lvAttrName = lvAttrSpec.getName();
  
             // Determine if the attribute is a simple attribute or a
             // business object container.
             if (lvAttrSpec.isObjectType())
             {
                 // Get the next token based on the BOCountSize
                 lvAttrMaxLength = mBOCountSize;
                 lvAttrValue =  this.getNextToken(mBOCountSize, true);
                 String lvBOType = lvAttrSpec.getTypeName();
                 Object lvAttrBOValue = null;
  
                 if (lvAttrSpec.isMultipleCard())
                 {
                     this.getMultipleCard(pvBO,lvAttrIndex,lvBOType,
                         lvAttrValue);
                 }
                 else {
                     this.getSingleCard(pvBO,lvAttrIndex,lvBOType,
                         lvAttrValue);
                 }
             }
             else 
             {
                 // Get the next token based on the MaxLength of the attribute
                 lvAttrMaxLength = lvAttrSpec.getMaxLength();
                 if (lvAttrMaxLength > 0)
                     lvAttrValue =  this.getNextToken(lvAttrMaxLength, false);
                 else
                     lvAttrValue = null;
 
                // For simple String attribute, set to null, set to 
                 // configured CxIgnore or CxBlank values, or set to the
                 // attribute value 
                 if (lvAttrValue == null )
                     pvBO.setAttrValue(lvAttrIndex, null); 
                 else if (lvAttrValue.equals(mCxIgnore)||
                       lvAttrValue.equals(""))
                     pvBO.setAttrValue(lvAttrIndex, null); 
                 else if (lvAttrValue.equals(mCxBlank)||
                       lvAttrValue.equals(" "))
                     pvBO.setAttrValue(lvAttrIndex, ""); 
                 else
                     pvBO.setAttrValue(lvAttrIndex, lvAttrValue); 
             }
         }
     }
 }
  
  
 // Populates a child container with values in the String
 protected void getMultipleCard(BusinessObjectInterface pvParentBO, 
     int pvParentAttrIndex, String pvBOType, String pvObjectCountString)
     throws CW_BOFormatException, Exception
 {
     try {
         if ( pvObjectCountString.equals(mCxIgnore) )
         {
             // trace message
         } 
         else {
             int lvObjectCount = Integer.parseInt(pvObjectCountString);
             if ( lvObjectCount == 0)
             {
                 // trace message with the number of objects in container
             }
             else if (lvObjectCount > 0)
             {
                 // There is at least one instance of the object in the string
                 BusinessObjectInterface lvChildBO = null;
  
                 // For each instance of the child object, parse the attribute
                 // list, and then add the object container to the parent.
                 for (int lvObjectIndex = 0; lvObjectIndex < lvObjectCount; 
                       lvObjectIndex++)
                 {
                     lvChildBO = getBOFromString(pvBOType); 
                     pvParentBO.setAttrValue(pvParentAttrIndex,lvChildBO);
                 } 
             }
         }
     }
 }
 
// Populates a single cardinality child with values in the String
 protected void  getSingleCard(BusinessObjectInterface pvParentBO, 
     int pvParentAttrIndex, String pvBOType, String pvObjectCountString)
     throws CW_BOFormatException, Exception
 { 
     try {
         BusinessObjectInterface lvChildBO = null;
             
         // Check the object count token
         // If it does not match "1", assume that the child object should 
         //  be null
         if (pvObjectCountString.equals("1"))
         {
             // The string contains a single instance of the child
             lvChildBO = getBOFromString(pvBOType);       
             pvParentBO.setAttrValue(pvParentAttrIndex, lvChildBO);
         }
         else if ( pvObjectCountString.equals(mCxIgnore) || 
               pvObjectCountString.equals("0"))
         {
             // Validate that the object count token is valid
         }
         else
             throw new CW_BOFormatException(...); 
     }
 }
 

Implementing conversion from a business object

The abstract methods in Table 76 perform the business-object-to-string conversion; that is, they each create serialized data in a particular format from a business object.

Table 76. Abstract methods to implement business-object-to-string conversion

Abstract method Description
getStringFromBO() Converts the data in a business object to a String object.
getStreamFromBO() Converts the data in a business object to an InputStream object.
getByteArrayFromBO() Cconverts the data in a business object to a byte array.

The goal of converting from a business object is to create a serialized form of all data in the business object. Sometimes, however, some business-object data should not be included in the serialized data. For example, a business object might use a child meta-object to hold dynamic configuration information for a connector.

IBM reserves all application-specific information that begins with the prefix cw_mo_label for configuration and/or dynamic metadata. To indicate any attribute that a data handler should ignore during conversion from a business object, the business object definition for the parent business object specifies the following tag in its application-specific information:

cw_mo_label=child_meta-object_attribute_name
 

where label is a string you define to further identify the purpose of the cw_mo_ tag and child_meta-object_attribute_name identifies the name of the attribute to ignore. This attribute usually contains the child meta-object. Multiple cw_mo_label tags are delimited by a semicolon (;).

When you implement the getStringFromBO(), getStreamFromBO(), and getByteArrayFromBO() methods for a custom data handler, these methods need to ensure that the data handler skips over connector-specific attributes, as follows:

  1. Check for the existence of any cw_mo_label tag (where label is a string you provide to identify the attribute to ignore) in the application-specific information of the business object definition for the business object being converted.
  2. If a cw_mo_label tag exists, locate the string that this tag provides (child_meta-object_attribute_name). Ignore any white space surrounding the equal sign (=).
  3. While looping through the attributes of the business object, compare each attribute name against the child_meta-object_attribute_name string. Skip over any attribute with this name.

The following code fragment shows how to skip over attributes:

List configObjList = 
      com.crossworlds.DataHandlers.text.namevalue.listMOAttrNames(BusObj);
  
 //this list contains attribute names, which are configuration objects
 for ( attributes in BusObj )
 {
           String attrName = BusObj.getAttrDisc(i).getName();
           if ( configObjList.contains(attrName) )
           {
             //skip
             continue;
           }
 }
 

For example, suppose a business object called MyCustomer uses a child meta-object to hold connector-specific routing information. If this meta-object is represented by an attribute named CustConfig, then MyCustomer could have the following tag in its application-specific information:

cw_mo_cfg=CustConfig
 

During conversion from a business object, a custom data handler checks the application-specific information for the business object definition associated with MyCustomer, locates the cw_mo_cfg tag, and determines that the CustConfig attribute needs to be skipped over. The resulting serialized data from the data handler does not include the CustConfig child meta-object.

Note:
When converting from a business object, IBM-delivered data handlers skip over any attributes that the cw_mo_label tag identifies.

You need to develop any custom data handler to handle cw_mo_label tags only if the data handler works with connectors that use child meta-objects or other dynamic objects.

An implementation of the getStringFromBO() method

The getStringFromBO() method performs the business-object-to-string conversion; that is, it converts the data in a business object to a String object. For this method, the caller passes in the business object to be converted. Figure 42 shows the getStringFromBO() method as implemented by the FixedWidth data handler. The method creates a String of fixed-width fields.

The example illustrates the steps in the conversion of data from a business object to a Reader object:

  1. The getStringFromBO() method calls setAttrList() to recursively loop through the attributes in the business object. When the setAttrList() method finds a simple attribute, it calls the setSimpleToken() method to set the value.
  2. The setSimpleToken() method adds the attribute value to a StringBuffer object, converts the StringBuffer to a String object, and adds the string to the StringBuffer representing the entire business object. When setAttrList() has processed the business object, the getStringFromBO() method converts the StringBuffer to a String object and returns the String to the caller.

Figure 42. Example getStringFromBO() method

public String getStringFromBO(BusinessObjectInterface theObj, 
     Object config)
     throws Exception
 {
     traceWrite( 
       "Entering getStringFromBO(BusinessObjectInterface, Object) "
       + " for object type " + theObj.getName(),
       JavaConnectorUtil.LEVEL4);
  
     clear(config);
     String lvBOString = null;
     setAttrList(theObj);
     lvBOString = mBOStringBuffer.toString();
  
     traceWrite( 
       "Exiting getStringFromBO(BusinessObjectInterface, Object) "
        + " for object type " + theObj.getName(),
        JavaConnectorUtil.LEVEL4);
  
     return lvBOString;
 }
  
  
 protected void setAttrList(BusinessObjectInterface pvBO) throws Exception
 {
     traceWrite( 
       "Entering setAttrList(BusinessObjectInterface) for object "
       + pvBO.getName(), JavaConnectorUtil.LEVEL4);
  
     int lvAttrNum = pvBO.getAttrCount();
  
     String lvAttrName = null;
     String lvAttrValue = null;
     int lvAttrMaxLength = 0;
  
     // Add the business object name and verb to the fixed width format
     //  String
     this.setSimpleToken( mBONameSize, pvBO.getName());
     this.setSimpleToken( mBOVerbSize, pvBO.getVerb());
  
     try {
         List moAttrNames = listMOAttrNames( pvBO );
         int lvAttrCount = pvBO.getAttrCount();
  
         ATTRIBUTE_WALK: for (int lvAttrIndex = 0; 
               lvAttrIndex < lvAttrCount; ++lvAttrIndex)
         {
             CxObjectAttr lvAttrSpec = pvBO.getAttrDesc(lvAttrIndex);
             lvAttrName = lvAttrSpec.getName();
  
             // Check if the current attribute is a simple (String) type
             //  or a contained object.
             if (lvAttrSpec.isObjectType())
             {
                 //skip child objects designated as meta objects
                 if( moAttrNames.contains( lvAttrName ) ) 
                 {
                     continue ATTRIBUTE_WALK;
                 }
 
            if (lvAttrSpec.isMultipleCard())
             {
                 CxObjectContainerInterface lvAttrMultiCardBOValue =
               (CxObjectContainerInterface) pvBO.getAttrValue(lvAttrIndex);
  
                 if (lvAttrMultiCardBOValue == null)
                 {
                     traceWrite( 
                 "setAttrList found empty multiple cardinality container " 
                 + lvAttrSpec.getTypeName(), JavaConnectorUtil.LEVEL5);
  
                     // Add the count to the fixed width String
                     this.setSimpleToken( mBOCountSize, "0");
                 }
                 else
                 {
                     int lvObjectCount =
                       lvAttrMultiCardBOValue.getObjectCount();
                     traceWrite( 
                   "setAttrList found multiple cardinality container " 
                   + lvAttrSpec.getTypeName() + " with " 
                   + lvObjectCount + " instances",
                   JavaConnectorUtil.LEVEL5);
  
                     // Add the count to the fixed width String
                     this.setSimpleToken( mBOCountSize,
                       Integer.toString(lvObjectCount));
  
                     // Add each object in the container to the fixed
                     //  width String.
                     for (int lvContObjectIndex = 0; 
                           lvContObjectIndex < lvObjectCount;
                           ++lvContObjectIndex)
                         setAttrList(
                     lvAttrMultiCardBOValue.getBusinessObject(
                         lvContObjectIndex));
                 }
             }
             else
             {
                 BusinessObjectInterface lvAttrSingleCardBOValue =
               (BusinessObjectInterface) pvBO.getAttrValue(lvAttrIndex);
  
                 if (lvAttrSingleCardBOValue == null)
                 {
                      traceWrite( 
                 "setAttrList found empty single cardinality container " 
                 + lvAttrSpec.getTypeName(), JavaConnectorUtil.LEVEL5);
  
                      // Add the count to the fixed width String
                      this.setSimpleToken( mBOCountSize, "0");
                 }
 
                else
                 {
                     traceWrite( 
                   "setAttrList found single cardinality container "
                   + lvAttrSpec.getTypeName(),
                   JavaConnectorUtil.LEVEL5);
  
                     // Add the count to the fixed width String
                     this.setSimpleToken( mBOCountSize, "1");
                     setAttrList(lvAttrSingleCardBOValue);
                 }
             }
         }
         else
         {
             lvAttrValue = (String) pvBO.getAttrValue(lvAttrIndex);
             lvAttrMaxLength = lvAttrSpec.getMaxLength();
  
             if (lvAttrMaxLength > 0)
                 this.setSimpleToken(lvAttrMaxLength, lvAttrValue);
            }
         }
     }
     catch (CxObjectNoSuchAttributeException e)
     {
         throw new Exception(e.getMessage());
     }
  
     traceWrite( 
         "Exiting setAttrList(BusinessObjectInterface) for object "
         + pvBO.getName(), JavaConnectorUtil.LEVEL4);
 }
  
  
 protected void setSimpleToken( int pvCellSize, String pvTokenValue)
     throws Exception
 {
     traceWrite( "Entering setSimpleToken(int, String)",
       JavaConnectorUtil.LEVEL4);
  
     StringBuffer lvNewBuffer = new StringBuffer(pvCellSize);
     int lvTokenLength = 0;
     int lvCxIgnoreLength = mCxIgnore.length();
     int lvCxBlankLength = mCxBlank.length();
  
     int lvPadNumber = 0;
  
  
     // Check the token value to see if it is null
     if (pvTokenValue == null)
     {
         // For this case, we add the configured CxIgnore value to the
         //  fixed width String if it fits in the cell.
         if (!mTruncation && lvCxIgnoreLength > pvCellSize)
             throw new Exception(
                 " Null attribute value encountered where cell size is "
                 + pvCellSize + ", size of CxIgnore value is "
                 + lvCxIgnoreLength + "and trucation is not allowed. " 
                 + "Please check your MO format configuration.");
 
        else
         {
             lvPadNumber = pvCellSize - lvCxIgnoreLength;
             lvNewBuffer.append(mCxIgnore);
         }
  
     }
     else if (pvTokenValue.equals(""))
     {
         // For this case, the configured CxBlank value is added to the
         //  fixed width String if it fits in the cell.
         if (! mTruncation && lvCxBlankLength > pvCellSize)
             throw new Exception(
               " Blank attribute value encountered where cell size is "
               + pvCellSize + ", size of CxBlank value is "
               + lvCxBlankLength + "and trucation is not allowed. " 
               + "Please check your MO format configuration.");
         else
         {
             lvPadNumber = pvCellSize - lvCxBlankLength;
             lvNewBuffer.append(mCxBlank);
         }
     }
     else
     {
         // For this case, actually add the token value to the fixed
         // width String, unless the data is too long for the cell.
         lvTokenLength = pvTokenValue.length();
         if (!mTruncation && lvTokenLength > pvCellSize )
             throw new Exception(
               " Attribute value encountered where cell size is "
               + pvCellSize + ", size of token value is "
               + lvTokenLength + "and trucation is not allowed. "
               + "Please check your MO format configuration.");
         else
         {
             lvNewBuffer.append(pvTokenValue);
             lvPadNumber = pvCellSize - lvTokenLength;
         }
     }
  
     if (lvPadNumber <= 0 && mTruncation)
         // Token is longer than the cell and truncation is allowed,
         //  so the characters up to pvCellSize are added
         lvNewBuffer.setLength(pvCellSize);
     else if (lvPadNumber > 0)
     {
         // Pad the cell based on the configuration option chosen
         if ( mAlignment.equals(fixedwidth.AlignmentLeft) ||
               mAlignment.equals(fixedwidth.AlignmentBoth))
             this.padRight(lvNewBuffer, lvPadNumber);
         else if (mAlignment.equals(fixedwidth.AlignmentRight))
             this.padLeft(lvNewBuffer, lvPadNumber);
     }
 
    String lvNewBuffString =  lvNewBuffer.toString();
  
     // Note that this may cause a performance issue when the tracing
     //  level is low, but in most cases it should not as any one token
     //  is *usually* not very long.
     traceWrite( 
       "Adding the following token to the fixed width String: "
       + lvNewBuffString, JavaConnectorUtil.LEVEL5);
  
     // After the cell has been fully formatted, append to fixed width
     //  String being built
     mBOStringBuffer.append(lvNewBuffString);
  
     traceWrite( "Exiting setSimpleToken(int, String)",
       JavaConnectorUtil.LEVEL4);
 }
 
An implementation of the getStreamFromBO() method

The getStreamFromBO() method converts the data in a business object to an InputStream object. Figure 43 shows an example implementation of the getStreamFromBO() method. In this implementation, getStreamFromBO() calls getStringFromBO() to build a String object containing the business object data, and then it converts the String to an InputStream. The method returns an InputStream object representing the data in the business object.

Figure 43. Example getStreamFromBO() method

public InputStream getStreamFromBO(BusinessObjectInterface theObj, 
     Object config)
     throws Exception
 {
     clear(config);
  
     String BOstring;
     BOstring = getStringFromBO(theObj, config);
  
     return new ByteArrayInputStream( BOstring.getBytes() );
 }
 

Overriding public methods

In addition to the abstract DataHandler methods (which you must implement), you might also need to override some public methods of the DataHandler class (see Table 77) so they work optimally with your custom data handler.

Table 77. Public methods of the DataHandler class

Public DataHandler method Description
getBO() - public Converts serialized data(in one of several formats) to a business object.
getBOName() Obtains the name of the business object from the serialized data.
getBooleanOption() Gets a value of a Boolean configuration option from the data handler.
getOption() Gets the value of a configuration option from the data handler.
setOption() Sets a configuration option in the data handler.
traceWrite() Calls a trace-write method for the appropriate context of the data handler: connector or the Server Access Interface (if your integration broker is InterChange Server).

Copyright IBM Corp. 1997, 2003