Manipulating messages in the BLOB domain

This topic provides information specific to dealing with messages that belong to the BLOB domain, and that are parsed by the BLOB parser.

You cannot manipulate the contents of a BLOB message, because it has no predefined structure. However, you can refer to its contents using its known position within the bit stream, and process the message with a minimum of knowledge about its contents.

The BLOB message body parser does not create a tree structure in the same way that other message body parsers do. It has a root element BLOB, that has a child element, also called BLOB, that contains the data.

You can refer to message content using substrings if you know the location of a particular piece of information within the BLOB data. For example, the following expression identifies the tenth byte of the message body:

InputBody.BLOB.BLOB[10]

The following expression references 10 bytes of the message data starting at offset 10:

SUBSTRING(InputBody.BLOB.BLOB from 10 for 10)

Example of BLOB message manipulation

This example shows how to manipulate a variable length BLOB message. The example assumes that you have configured a message flow that receives a variable length BLOB message, parses some of the fields by invoking the MRM parser, and routes the output message to the correct output queue based on the information parsed.

The input message is in BLOB format and is assumed to contain embedded NULLs ('x00'), so it cannot be defined as null terminated.

This example shows the ESQL needed to:

  • Calculate the BLOB message length
  • Convert it to hexadecimal format
  • Add it to the beginning of the BLOB message

By doing this, you can define the message model with an integer length field followed by the BLOB message.

This example also shows how to convert the BLOB message to CWF, process the message, and strip off the added length field.

In this example the input record has the following format:

  • Version Number: string, 11 characters
  • Store Number: string, 10 characters

    This field is used as an integer to route the message to different queues depending on customer-defined criteria.

  • Store Data: variable length binary data

Define a new message

Define a new message BLOB_Example that includes the following elements and types:

  • B_LEN, xsd:integer
  • VERSION_NUM, xsd:string, Length 11
  • STORE_NUM, xsd:string, Length 10
  • BIN_BLOB, xsd:binary, Length Value B_LEN

Create a message flow

This section describes the characteristics of the message flow. If you want to implement this example flow, you must complete the message flow definition (for example, by creating the three subflows to replace the output nodes used here to handle false, unknown, and failure cases) and provide any required support for its deployment and execution (for example, creating the inbound and any outbound queues on the queue manager for the broker to which you deploy the flow).

  1. Create the subflow LESS_THAN. This task is described in Create LESS_THAN subflow.
  2. Create a new message flow. Add nodes to the message flow editor view: an MQInput node, a Compute node, a ResetContentDescriptor node, a Filter node, three MQOutput nodes, and the LESS_THAN subflow.
  3. Change the name of the MQInput node to INQUEUE and set its Queue Name property to INQUEUE.
  4. Connect the output terminal to the Compute node.
  5. Change the Compute node name from its default value to Add_length. Configure the Compute node to calculate the length of the BIN_BLOB and add it to the beginning of the BLOB_Example message in field B_LEN:
    1. Right-click the node and click Open ESQL.
    2. Code the following ESQL in the module for this node:
      -- Declare local variables
      DECLARE I      INTEGER 1;
      DECLARE J      INTEGER CARDINALITY(InputRoot.*[]);
      DECLARE MSGLEN CHARACTER; 
      DECLARE NUMBER INTEGER; 
      DECLARE RESULT INTEGER; 
      DECLARE REM    INTEGER; 
      
      -- Copy message headers
      WHILE I < J DO
        SET OutputRoot.*[I] = InputRoot.*[I];
        SET I = I + 1;
      END WHILE;
      -- 
      -- Set MSGLEN to non NULL to avoid errors when concatenating the first time -- 
      SET MSGLEN = 'X'; 
      -- 
      -- Get the length of the BLOB and substract the length of VERSION_NUM and STORE_NUM (11+10) 
      SET NUMBER = LENGTH("InputRoot"."BLOB"."BLOB")-21; 
      -- 
      -- Convert NUMBER to hexadecimal. The remainder of dividing by 16 is calculated recursively. -- 
      WHILE NUMBER > 15 DO      
         SET RESULT = NUMBER/16;      
         SET REM    = NUMBER - RESULT*16;      
         SET MSGLEN = 
          CASE
            WHEN REM < 10  THEN CAST(REM AS CHARACTER) || MSGLEN               
            WHEN REM = 10  THEN 'A' || MSGLEN              
            WHEN REM = 11  THEN 'B' || MSGLEN              
            WHEN REM = 12  THEN 'C' || MSGLEN              
            WHEN REM = 13  THEN 'D' || MSGLEN               
            WHEN REM = 14  THEN 'E' || MSGLEN              
            ELSE                'F' || MSGLEN          
          END;      
         SET NUMBER = RESULT; 
      END WHILE; 
      SET REM = NUMBER;     
      SET MSGLEN =           
         CASE                
           WHEN REM < 10  THEN CAST(REM AS CHARACTER) || MSGLEN               
           WHEN REM = 10  THEN 'A' || MSGLEN              
           WHEN REM = 11  THEN 'B' || MSGLEN              
           WHEN REM = 12  THEN 'C' || MSGLEN              
           WHEN REM = 13  THEN 'D' || MSGLEN               
           WHEN REM = 14  THEN 'E' || MSGLEN              
           ELSE                'F' || MSGLEN          
         END; 
      -- 
      -- Add leading '0's up to a length of 9 to be able to cast as BLOB. 
      -- Remember it started with MSGLEN set to X (length 1) 
      WHILE LENGTH(MSGLEN) < 9 DO       
         SET MSGLEN = '0' || MSGLEN; END WHILE; 
      -- 
      -- Change to appropriate endian (PLATFORM DEPENDENT) 
      -- If no endian swapping needed then remember to get rid of the last character as below -- 
      SET MSGLEN = SUBSTRING(MSGLEN FROM 1 FOR 8); 
      -- 
      SET MSGLEN = SUBSTRING(MSGLEN FROM 7 FOR 2) || SUBSTRING(MSGLEN FROM 5 FOR 2) ||
                   SUBSTRING(MSGLEN FROM 3 FOR 2) || SUBSTRING(MSGLEN FROM 1 FOR 2);
      SET "OutputRoot"."BLOB"."BLOB" = CAST(MSGLEN AS BLOB) || "InputRoot"."BLOB"."BLOB"; 
      
  6. Connect the out terminal of the Compute node to the ResetContentDescriptor node.
  7. Change the ResetContentDescriptor node name to ResetContent_2_MRM. Configure the node as follows:
    1. Set Message Domain to MRM.
    2. Select the Reset Message Domain check box.
    3. Set Message Set to the identifier of the message set in which you defined the BLOB_Example message.
    4. Select the Reset Message Set check box.
    5. Set Message Type to BLOB_Example.
    6. Select the Reset Message Type check box.
    7. Set to the name of the CWF physical format that you have defined (for example, the default value CWF1).
    8. Select the Reset Message Format check box.
  8. Connect the out terminal of the ResetContentDescriptor node to the Filter node.
  9. Change the name of the Filter node to Route_2_QUEUE. Configure the node as follows:
    1. Right-click the node and click Open ESQL.
    2. Code the following ESQL statement in the ESQL module for this node:
      CAST("Body"."e_STORE_NUM" AS INTEGER) < 151    

      This statement is based on the arbitrary assumption that an incoming message from a Store Number is less than 151 and is routed to a specific queue. You can code any other suitable test.

  10. Connect the Filter output terminals as follows:
    1. True terminal to a subflow node (see below) named LESS_THAN.
    2. False terminal to an MQOutput node named GREATER_THAN with Queue Name property set to GREATER_THAN.
    3. Unknown terminal to an MQOutput node named INVALID with Queue Name property set to INVALID.
    4. Failure to an MQOutput node named ERROR with Queue Name property set to ERROR.

Create LESS_THAN subflow

This subflow handles a message that has the expected format (the test performed in the Filter node returned true). The successful message is written to the output queue in its original form; the message is converted back to BLOB from MRM and the four bytes that were added (field B_LEN) are removed.

For this subflow:

  1. Create a new message flow named LESS_THAN.
  2. In the editor view, add an Input node, a ResetContentDescriptor node, a Compute node, and an MQOutput node.
  3. Change the name of the Input node to InputTerminal1 and connect its out terminal to the ResetContentDescriptor node.
  4. Change the name of the ResetContentDescriptor to ResetContent_2_BLOB and configure the node:
    1. Set Message Domain to BLOB
    2. Select the Reset Message Domain check box.
  5. Connect the ResetContentDescriptor node out terminal to the Compute node.
  6. Change the name of the Compute node to Remove_length and configure the node:
    1. Right-click the node and click Open ESQL.
    2. Code the following ESQL in the module for this node:
      -- Copy message headers
      DECLARE I INTEGER 1;
      DECLARE J INTEGER CARDINALITY(InputRoot.*[]);
      
      WHILE I < J DO
        SET OutputRoot.*[I] = InputRoot.*[I];
        SET I = I + 1;
      END WHILE;
      -- 
      -- Remove the 4 bytes length field added previously -- 
      SET "OutputRoot"."BLOB"."BLOB" = SUBSTRING("InputRoot"."BLOB"."BLOB" FROM 5);

      This Compute node removes the four bytes that were added at the beginning of the BLOB message to support its manipulation.

      Note the use of a variable, J, initialized to the value of the cardinality of the existing headers in the message. This is more efficient than calculating the cardinality on each iteration of the loop, which happens if you code the following WHILE statement:

      WHILE I < CARDINALITY(InputRoot.*[]) DO
  7. Connect the out terminal of the Compute node to the MQOutput node.
  8. Change the name of the MQOutput node to Output_success and configure the node, setting Queue Manager Name and Queue Name. You might find it helpful to promote these MQOutput node properties so that you can specify them at the message flow level.