Populating MQ headers in WebSphere Enterprise Service Bus using a custom MQ SendExit

The WebSphere MQ binding provided with WebSphere ESB and WebSphere Process Server enables communication between native MQ applications and a Service Component Architecture (SCA) environment. The WebSphere MQ bindings simplify the sending and receiving of MQ messages, but MQ header field cannot be populated with MQ bindings in WebSphere Integration Developer. This article shows you how to use a custom MQ SendExit to populate MQ headers required by native MQ applications, and includes sample code to set AppIdentityData in the MQ header.

Share:

Jun Shen (junshen@au1.ibm.com), Senior Consultant, WebSphere Lab Services team, IBM

Photo of Jun ShenJun Shen is a Senior Consultant on the WebSphere Lab Services team. He specializes in WebSphere Process Server and SOA implementations, and has 10 of experience in the IT industry in China and Australia. He received a Ph.D. in Computer Science from Tsinghua University in China in 1999. You can contact Jun at junshen@au1.ibm.com.



13 June 2012

Also available in Chinese Russian

Introduction

IBM® WebSphere® MQ bindings in IBM WebSphere ESB let you send and receive messages via WebSphere MQ. The MQ binding automatically adds an MQ Message Descriptor (MQMD) to any messages that don't have one, but does not modify the MQMD. The MQHeaderSetter primitive can be used to create and change MQ headers to a message, but some fields cannot be populated in WebSphere ESB V6.2, including:

  • UserIdentifier
  • AppIdentityData
  • PutApplType
  • PutApplName
  • ApplOriginData

Although these MQMD fields can be propagated by setting the custom property MDCTX=SET_ALL_CONTEXT on the JNDI queue definition in WebSphere ESB V7 or later, a custom MQ SendExit is introduced in this paper to solve this issue for WebSphere ESB V6.2. The following products are used in this article:

  • WebSphere Integration Developer V6.2.0.2
  • WebSphere ESB V6.2.0.2 Integrated Test Environment (included with WebSphere Integration Developer)
  • WebSphere MQ V6.0.2.9

Populating MQMD using a custom MQ SendExit

A sample project will show you how to write a custom MQ SendExit to populate AppIdentityData in MQMD:

Building the module

First, create an MQ binding test module:

  1. Create a new module called MQHeaderTest. Ensure that Create mediation component is checked
  2. Open the Dependencies on the library, and under Predefined Resources, check Schema for simple JMS Data Bindings, and then click Save. The JMS body types are now available.
  3. In the module, create a new interface called MQHeaderTestService.
  4. Create a one-way operation named sendMessage.
  5. Change the type of input1 to JMSTextBody and click Save to save the interface:
    Figure 1. MQHeaderTestService Interface
    MQHeaderTestService Interface
  6. Create a business object called MQTestMessage:
    Figure 2. MQTestMessage Business Object
    MQTestMessage Business Object
  7. Create another interface called TestRequestService:
    Figure 3. TestRequestService Interface
    TestRequestService Interface
  8. Right-click the mediation component, select Add: Interface, and select TestRequestService.
  9. Add an Import to the assembly diagram.
  10. Wire the mediation component to the Import, and when asked to add a reference, select MQHeaderTestService.
  11. Right-click Import and select Generate Binding => Messaging Binding => MQ Binding.
  12. Specify the JNDI name for MQ connection factory and queue, and then click OK:
    Figure 4. MQ binding
    MQ binding

    The assembly diagram is shown in Figure 5:

    Figure 5. MQHeaderTest assembly
    MQHeaderTest assembly

Building the mediation flow

Next, create the mediation flow to convert the value of appIdentityData and payload in the MQTestMessage business object into a string, whose structure is shown below. The purpose of this operation is to pass the appIdentityData value to the custom MQ SendExit via the MQ message content.

Figure 6. MQTestMessage string structure
MQTestMessage string structure
  1. Double-click on the mediation component MQHeaderTest to generate an implementation.
  2. In the Mediation Flow Editor, wire request to sendMessage to create a flow.
  3. In the Palette, select Transformation => Business Object Map.
  4. Add the custom mediation to the canvas.
  5. Wire the input node to the input terminal of the custom mediation.
  6. Wire the output terminal of the custom mediation to the callout node. Your mediation flow is shown in Figure 7:
    Figure 7. MQHeaderTest mediation flow
    MQHeaderTest mediation flow
  7. Click BOMapper1 to implement the mapping:
    Figure 8. BOMapper1 implementation
    BOMapper1 implementation
  8. Implement the custom map with the code in Listing 1:
    Listing 1. Custom code for BOMapper1
    //get the length of appIdentityData
    String appIDLength = 
     StringUtils.getHexString(((String)requestRequestMsg_request_input_appIdentityData) 
    .length());
    
    sendMessageRequestMsg_sendMessage_input1_value = appIDLength + 
        (String)requestRequestMsg_request_input_appIdentityData + 
        (String)requestRequestMsg_request_input_payload;

Building the custom MQ SendExit

Create a new library named MQExitLib and a new Java class named MQExitImp in the com.ibm.test.mqexit package to implement MQ SendExit:

Listing 2. Custom MQ SendExit
package com.ibm.test.mqexit;

import java.io.ByteArrayOutputStream;

import com.ibm.mq.MQC;
import com.ibm.mq.MQChannelDefinition;
import com.ibm.mq.MQChannelExit;
import com.ibm.mq.data.MQDataOutputStream;
import com.ibm.ws.sca.internal.mq.exit.MQInternalSendExitImpl;

public class MQExitImp extends MQInternalSendExitImpl {
    
    public static final int mqQueueType = 131;
    public static final int queueControlOffset = 212;
    public static final int applIDControlOffset = 416;
    public static final int applIDOffset = 284;
    public static final int dataLengthOffset = 536;
    public static final int dataOffset = 540;

    public MQExitImp(String version) {
        super("Centrelink MQ SendExit implementation");
    }

    /**
     * 
     * Data format: AppIDLength(2 chars) + AppID + AppData
     */
    public byte[] sendExit(MQChannelExit exit, MQChannelDefinition chl,
            byte[] buf) {

        byte[] msg = super.sendExit(exit, chl, buf);

        if (exit.exitReason == MQChannelExit.MQXR_XMIT) {

            //send data
            if (byteToInt(buf[controlFlag2]) == mqputRequest) {
                int controlFlag = byteToInt(buf[controlFlag3]);
                if ((controlFlag == firstSegment)||(controlFlag == 
onlySegment)) {

                    System.out.println(">>>>> Queue data input = " + 
StringUtils.getHexString(msg));
                    System.out.println(">>>>> remoteUserId = " + 
chl.remoteUserId);

                    //process message data
                    boolean reversed = (byteToInt(msg[controlFlag1]) == 
mqFapReversed);
                    int ccsid = readShortInt(msg, mqFapCcsidOffset, 
    reversed);

                    int encoding = readInt(msg, mqFapMqEncOffset,
                            reversed);

                    //set IDENTITY_CONTEXT
                    int exitOptions = readInt(msg, applIDControlOffset, 
    reversed);
                    System.out.println(">>>>> Original Options = " + 
exitOptions);

                    if ((exitOptions & MQC.MQPMO_PASS_ALL_CONTEXT) == 
MQC.MQPMO_PASS_ALL_CONTEXT) return msg; 

                    ByteArrayOutputStream baos = null;
                    MQDataOutputStream mqdos = null;
                    try {
                        //set AppID
                        String appIdLenStr = new String(msg, 
dataOffset, 2);
                        int appIDLen = 0;
                        try {
                            appIDLen = 
Integer.parseInt(appIdLenStr);
                        } catch (Exception ex) {
                            System.out.println(">>>>> appIDLen 
exception =  " + ex.getMessage());
                            return msg;
                        }
                        System.out.println(">>>>> appIDLen = " + 
appIDLen);

                        //set options
                        baos = new ByteArrayOutputStream();
                        mqdos = new MQDataOutputStream(baos);
                        mqdos.setEncoding(encoding);
                        mqdos.setCCSID(ccsid);
                        mqdos.writeMQLONG(exitOptions | 
MQC.MQPMO_SET_IDENTITY_CONTEXT);

                        byte[] valBytes = baos.toByteArray();

                        System.out.println(">>>>> New Options = " + 
StringUtils.getHexString(valBytes));

                        System.arraycopy(valBytes, 0, msg,
                                applIDControlOffset,
                                valBytes.length);

                        //set AppID
                        String appID = new String(msg, dataOffset+2, 
appIDLen);

                        valBytes = appID.getBytes();

                        System.out.println(">>>>> AppID = "   + 
StringUtils.getHexString(valBytes));

                        System.arraycopy(valBytes, 0, msg,
                                applIDOffset,
                                valBytes.length);

                        //set AppData Length
                        int dataLength = readInt(msg, 
dataLengthOffset,   reversed);
                        System.out.println(">>>>> Original dataLength 
= " + dataLength);

                        baos = new ByteArrayOutputStream();
                        mqdos = new MQDataOutputStream(baos);
                        mqdos.setEncoding(encoding);
                        mqdos.setCCSID(ccsid);
                        mqdos.writeMQLONG(dataLength - appIDLen -2);

                        valBytes = baos.toByteArray();

                        System.out.println(">>>>> New dataLength = " 
+ StringUtils.getHexString(valBytes));

                        System.arraycopy(valBytes, 0, msg,
                                dataLengthOffset,
                                valBytes.length);

                        //set AppData
                        byte[] newMsg = new byte[msg.length - 
appIDLen -2];
                        //copy header
                        System.arraycopy(msg, 0, newMsg,
                                0,
                                dataOffset);

                        //copy data
                        System.arraycopy(msg, dataOffset + appIDLen + 
2, newMsg,
dataOffset,
                                dataLength - appIDLen -2);

                        System.out.println(">>>>> Queue data output = 
"   + StringUtils.getHexString(newMsg));

                        return newMsg;

                    } catch (Exception e) {
                        e.printStackTrace(System.out);
                    } finally {
                        if (mqdos != null) {
                            try {
                                mqdos.close();
                            } catch(Exception e1) {}
                        }
                        if (baos != null) {
                            try {
                                baos.close();
                            } catch(Exception e1) {}
                        }
                    }
                }
            } else    if (byteToInt(buf[controlFlag2]) == mqQueueType) {
                System.out.println(">>>>> Queue connection input = " +
StringUtils.getHexString(msg));
                System.out.println(">>>>> remoteUserId = " + 
chl.remoteUserId);

                //process queue control
                boolean reversed = (byteToInt(msg[controlFlag1]) == 
mqFapReversed);
                int ccsid = readShortInt(msg, mqFapCcsidOffset,
                        reversed);

                int encoding = readInt(msg, mqFapMqEncOffset,
                        reversed);

                int exitOptions = readInt(msg, queueControlOffset,
                        reversed);

                System.out.println(">>>>> Original Options = " + 
exitOptions);
                if ((exitOptions & MQC.MQOO_PASS_ALL_CONTEXT) == 
MQC.MQOO_PASS_ALL_CONTEXT) return msg; 

                if ((exitOptions & MQC.MQOO_OUTPUT) == MQC.MQOO_OUTPUT) {
                    ByteArrayOutputStream baos = null;
                    MQDataOutputStream mqdos = null;
                    try {
                        baos = new ByteArrayOutputStream();
                        mqdos = new MQDataOutputStream(baos);
                        mqdos.setEncoding(encoding);
                        mqdos.setCCSID(ccsid);
                        mqdos.writeMQLONG(exitOptions | 
MQC.MQOO_SET_IDENTITY_CONTEXT);

                        byte[] valBytes = baos.toByteArray();

                        System.out.println(">>>>> New Options = " + 
StringUtils.getHexString(valBytes));

                        System.arraycopy(valBytes, 0, msg,
                                queueControlOffset,
                                valBytes.length);

                        System.out.println(">>>>> Queue connection 
output = " + StringUtils.getHexString(msg));

                    } catch (Exception e) {
                        e.printStackTrace(System.out);
                    } finally {
                        if (mqdos != null) {
                            try {
                                mqdos.close();
                            } catch(Exception e1) {}
                        }
                        if (baos != null) {
                            try {
                                baos.close();
                            } catch(Exception e1) {}
                        }
                    }
                }
            }
        }
        return msg;
    }
}

The custom SendExit class extends com.ibm.ws.sca.internal.mq.exit.MQInternalSendExitImpl. Change two cases to change the MQ message buffer:

  1. If the message type is mqQueue, set MQOO_SET_IDENTITY_CONTEXT for the queue options.
  2. If the message type is mqputRequest, set MQPMO_SET_IDENTITY_CONTEXT will be set for the message options. The appIdentityData value will be retrieved from the message content and populated in the message buffer. The appIdentityData value will be removed from the message content data.

Deploying the custom MQ SendExit

Installing the SendExit JAR file

  1. Export the Java project as a JAR file.
  2. Copy the JAR file into the directory <WESB_HOME>/lib/ext, where WESB_HOME is the installation directory of WebSphere ESB.

Configuring the MQ Queue Connection Factory

  1. Open the WebSphere Process Server admin console.
  2. Navigate to Resources => JMS Providers => WebSphere MQ => WebSphere MQ queue connection factories => MQHeaderTest_QCF => Custom properties.
  3. Select the existing property SENDEXIT, and replace the current value with com.ibm.test.mqexit.MQExitImp:
    Figure 9. MQ Queue Connection Factory custom properties
    MQ Queue Connection Factory custom properties

Testing the module

Use the universal test client to test the module:

  1. Right-click on the MQHeaderTest mediation component and select Test component.
  2. The body of the message is not important, so click Continue.
  3. In the Deployment Location window, select WebSphere ESB Server and click Finish. The server will start and the application will be deployed and invoked. After the component test is complete, you will see the response:
    Figure 10. Test component
    Test component
  4. Using WebSphere MQ Explorer, right-click on the Send queue and select Browse Messages.
  5. Double-click on the message, and you will see the application identity data set in the mediation component:
    Figure 11. MQMessage
    MQMessage

Here is the trace log:

Listing 3. Trace log
[15/07/10 15:47:05:110 EST] 00000047 SystemOut     O   >>>>> 
Queue connection input = 
54534820000000D80183300000000000000000000000011103520000000000D8000000000000000000000000
4F44202000000001000000052020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020414D512E2A20202020202020202020202020202020202020
20202020202020202020202020202020202020202020202020202020202020202020202000000020
[15/07/10 15:47:05:110 EST] 00000047 SystemOut     O   >>>>> 
remoteUserId = Administrator
[15/07/10 15:47:05:110 EST] 00000047 SystemOut     O   >>>>> 
Original Options = 32
[15/07/10 15:47:05:515 EST] 00000047 SystemOut     O   >>>>> 
Queue connection input = 
54534820000000D80183300000000000000000000000011103520000000000D8000000000000000000000000
4F44202000000001000000052020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020414D512E2A20202020202020202020202020202020202020
20202020202020202020202020202020202020202020202020202020202020202020202000000020
[15/07/10 15:47:05:515 EST] 00000047 SystemOut     O   >>>>> 
remoteUserId = Administrator
[15/07/10 15:47:05:515 EST] 00000047 SystemOut     O   >>>>> 
Original Options = 32
[15/07/10 15:47:05:671 EST] 00000047 SystemOut     O   >>>>> 
Queue connection input = 
54534820000000D80183300000000000000000000000011103520000000000D8000000000000000000000000
4F4420200000000100000001736D62496E202020202020202020202020202020202020202020202020202020
20202020202020202020202020202020514D5F69626D5F6C3374303534382020202020202020202020202020
2020202020202020202020202020202020202020414D512E2A20202020202020202020202020202020202020
20202020202020202020202020202020202020202020202020202020202020202020202000002010
[15/07/10 15:47:05:671 EST] 00000047 SystemOut     O   >>>>> 
remoteUserId = Administrator
[15/07/10 15:47:05:671 EST] 00000047 SystemOut     O   >>>>> 
Original Options = 8208
[15/07/10 15:47:05:718 EST] 00000047 SystemOut     O   >>>>> 
New Options = 00002410
[15/07/10 15:47:05:718 EST] 00000047 SystemOut     O   >>>>> 
Queue connection output = 
54534820000000D80183300000000000000000000000011103520000000000D8000000000000000000000000
4F4420200000000100000001736D62496E202020202020202020202020202020202020202020202020202020
20202020202020202020202020202020514D5F69626D5F6C3374303534382020202020202020202020202020
2020202020202020202020202020202020202020414D512E2A20202020202020202020202020202020202020
20202020202020202020202020202020202020202020202020202020202020202020202000002410
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
Queue data input = 
545348200000023901863000000000000000000000000111035200000000023900000000000000000071FA40
4D442020000000020000000000000008FFFFFFFF0000000000000111000004B82020202020202020FFFFFFFF
0000000200000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000002020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020000000000000000000000000
0000000000000000000000000000000000000000202020202020202020202020202020202020202020202020
2020202020202020000000002020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202000000000000000000000000000000000000000000000000000000001
0000000000000000FFFFFFFF504D4F200000000100002002FFFFFFFF00000000000000000000000000000001
2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
20202020202020200000001D3036546573744944546869732069732061207465737420737472696E67
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
remoteUserId = Administrator
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
Original Options = 8194
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
appIDLen = 6
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
New Options = 00002402
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
AppID = 546573744944
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
Original dataLength = 29
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
New dataLength = 00000015
[15/07/10 15:47:05:873 EST] 00000047 SystemOut     O   >>>>> 
Queue data output = 54534820000002390186300000000000000000000000011103520000000002390000
0000000000000071FA404D442020000000020000000000000008FFFFFFFF0000000000000111000004B82020
202020202020FFFFFFFF00000002000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020202020202020202020200000
0000000000000000000000000000000000000000000000000000000000005465737449442020202020202020
2020202020202020202020202020202020200000000020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020000000000000000000000000000000000000
000000000000000000010000000000000000FFFFFFFF504D4F200000000100002402FFFFFFFF000000000000
0000000000000000000120202020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020202020202020202020202020202020202020202020202020202020202020202020
20202020202020202020202020202020202000000015546869732069732061207465737420737472696E67

Conclusion

This article has explained how to use a custom MQ SendExit to populate MQ headers required by native MQ applications


Download

DescriptionNameSize
Code sampleMQHeaderTestPI.zip32 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=820948
ArticleTitle=Populating MQ headers in WebSphere Enterprise Service Bus using a custom MQ SendExit
publish-date=06132012