Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

IBM WebSphere MQ with C#: GUI application that is both GET REQUEST/ PUT RESPONSE and PUT REQUEST/ GET RESPONSE

0.00/5 (No votes)
9 Nov 2005 1  
An article on synchronization of a GET REQUEST/ PUT RESPONSE MQ C# program and a PUT REQUEST/ GET RESPONSE MQ C# program.

Introduction

Synchronizing MQ GETs and PUTs with C#

Legacy MQ code is written in C/C++. In a C++ program, you would link MQM.LIB and call its methods to do a GET/ PUT. The techniques of synchronization between a simple GET REQUEST/ PUT RESPONSE processor program and another simple PUT REQUEST/ GET RESPONSE client program are age old. But what .NET MQ developers would die for is a simple step by step guide on how to implement those well known techniques in C#. Well, IBM has provided their .NET equivalent library of MQM: AMQMDNET.DLL. This article will show you how to use this library to write simple MQ programs in C#.NET. I am thankful to another CodeProject article by John Concannon which you can find here. I have used a lot of his code - as such his article is enough to understand the synchronization part of it. What I have added is the concept of having "synchronization" under the umbrella of a single program. The sample application spawns a user thread that behaves as the server. Then, it behaves as the client. With a few mouse clicks, you can control the way the client and the server interact. Synchronization will be more visible and controlled.

Pre-requisites

If you have not installed WebSphere MQ, you are probably not going to need this article. I have worked with IBM WebSphere 5.3. IBM also provides the ".NET interoperability platform" along with 5.3. That, of course, needs to be installed - but you don't have to worry about that if you have WebSphere 5.3 or higher installed. Next, you will need AMQMDNET.DLL. If you don't find it, I have included it in the demo project zip file. Well, once you are ready with all the software you need, create your MQ Queue Manager and at least two queues. One will be the request queue and the other, the response queue. Note down the names of the queue manager and the queues. When the demo application is run, its UI, by default will contain the names that I had used during development. What you can do is, you can modify the code (the form) to put in your own values, so that you don't need to type them again.

MQ service must be running - (that goes without saying) for this piece of code to work.

Using the code

Well, the best way to understand how to use this code is to understand what the demo application does. You have two distinct group boxes - one for GET REQUEST PUT BACK REPLY (= GRPBR) and the other for PUT REQUEST GET BACK REPLY (= PRGBR). The former is the server and the latter the client. For meaningful functioning, you have to start the server first - so (after you have downloaded the demo program and kept the EXE in the same folder with amqmdnet.dll, which accompanies the EXE in the zip) go ahead and click the GET button. If you have typed in the correct queue manager and the request queue name, the GET button click will spawn a thread which is the "server" thread. It will start polling the request Queue for an incoming message. Keeping the "wait infinitely" box checked before clicking GET will ensure that the server runs infinitely, so that you can relax and trigger the client. Note that had I not done this GET operation in a user thread, the application would have hung, and you would not have been able to interact with the GUI any more to test the client.

So, now that somebody is listening for an incoming message - let us PUT a message. The PRGBR group box contains the PUT button. Type in the correct queue manager and queue names, and type in some request message text, and click PUT. Immediately, the message will be picked up by the server thread. The thread will exit, and the message will be displayed in the GRPBR "Request Text" box. The server, before terminating, will reply back with some message (the text of which you can supply in the UI), and the client will get it back. But before the client picks up the reply, it will ask you - "do you want me to poll for a reply"? I added this message just in case you are testing the client without spawning the server, so that you can click CANCEL in that case to avoid a wait you know will be futile.

The dialog box has instructions at its bottom pane on how to test this application in two ways - simple and complex. I just described the complex method above. The CS file MQRCText.CS was borrowed from John Concannon - you need it to show/log meaningful errors.

Here is the client processing code, abbreviated for pasting here with the catch blocks cut short (PRGBR PUT button click): The running comments identify the "steps" clearly:

//Step 1. Create Queue Manager Object. 

//This will also CONNECT the Queue Manager

try
{
    mqQMgr = new MQQueueManager( strQueueManagerName );
}
catch( MQException mqe )
{
    string strError = mqrcText.getMQRCText( mqe.Reason );
    MessageBox.Show( "Error trying to create Queue " + 
                "Manager Object. Error: " + mqe.Message + 
                ", Details: " + strError );
    return;
}
//Step 2. Open Request Queue for writing our request

try
{
    requestQueue = mqQMgr.AccessQueue( strRequestQueueName,
                    MQC.MQOO_OUTPUT // open queue for output

                    + MQC.MQOO_FAIL_IF_QUIESCING ); // but not if 

                                                    // MQM stopping

}
catch( MQException mqe )
{
    //...

}
//Step 3. Open Response Queue for reading the response.

//Note: You can do this AFTER writing the request too. 

//Depends on the protocol you are using - may be you 

//are not supposed to write the request unless you

//ensure that you are listening for response too. If 

//that is not the case, you can do this after writing 

//the request message (before step 5).

try
{
    responseQueue = mqQMgr.AccessQueue( strResponseQueueName,
        MQC.MQOO_INPUT_AS_Q_DEF         // open queue for input

        + MQC.MQOO_FAIL_IF_QUIESCING ); // but not if MQM stopping

}
catch( MQException mqe )
{
    //...

}
//Step 4. PUT Request Message in Request Queue. Note the options 

//needed to be set. Note that once PUT is successful, you can close 

//the Request Queue. Note that you are asking whoever receives this 

//request to copy the MSG ID to CORREL ID so that you can later 

//"match" the response you get with the request you sent.

try
{
    requestMessage = new MQMessage();
    requestMessage.WriteString( strRequestText );
    requestMessage.Format = MQC.MQFMT_STRING;
    requestMessage.MessageType = MQC.MQMT_REQUEST;
    requestMessage.Report = MQC.MQRO_COPY_MSG_ID_TO_CORREL_ID;
    requestMessage.ReplyToQueueName = strResponseQueueName;
    requestMessage.ReplyToQueueManagerName = strQueueManagerName;
    requestQueue.Put(requestMessage);
    if( requestQueue.OpenStatus )
        requestQueue.Close();
    if( MessageBox.Show("Request Message PUT successfully.\nOK: " + 
      "wait for RESPONSE, Cancel: skip waiting", "Do you want to " + 
      "wait for response?", MessageBoxButtons.OKCancel ) == 
                                               DialogResult.Cancel)
    {
        if( responseQueue.OpenStatus )
            responseQueue.Close();
        if( mqQMgr.ConnectionStatus )
            mqQMgr.Disconnect();
        return;
    }
}
catch( MQException mqe )
{
    //...

}
//Step 5. Read the response from response 

//queue. Note the options to be set.

//It may happen that you get no response. 

//Also note that you decide how long you wait

//for the response. In order to get the 

//response, note the "matching" criterion.

try
{
    responseMessage = new MQMessage();
    responseMessage.CorrelationId = requestMessage.MessageId;
    MQGetMessageOptions gmo = new MQGetMessageOptions();
    gmo.Options = MQC.MQGMO_WAIT;
    if( nud_PRGBR_WaitMS.Enabled == true )
        gmo.WaitInterval = ( int ) nud_PRGBR_WaitMS.Value;
    else
        gmo.WaitInterval = MQC.MQWI_UNLIMITED;
    gmo.MatchOptions = MQC.MQMO_MATCH_CORREL_ID;

    responseQueue.Get(responseMessage, gmo);
    tb_PRGBR_RESPONSE_TEXT.Text = 
      responseMessage.ReadString(responseMessage.MessageLength);
}
catch( MQException mqe )
{
    //...

}
//Step 6. Close Response Queue, Disconnect Manager. 

//Note that you have already closed the request 

//queue in step 4.

if( responseQueue.OpenStatus )
    responseQueue.Close();
if( mqQMgr.ConnectionStatus )
    mqQMgr.Disconnect();
return;

Here is the server processing code, abbreviated again (GRPBR GET button click, executed in the user thread): The running comments identify the "steps" clearly:

//Step 1. Create Queue Manager Object. 

//This will also CONNECT the Queue Manager

try
{
    mqQMgr = new MQQueueManager( strQueueManagerName );
}
catch( MQException mqe )
{
    //...

}
//Step 2. Open Request Queue for reading/getting the request

try
{
    requestQueue = mqQMgr.AccessQueue( strRequestQueueName,
        MQC.MQOO_INPUT_AS_Q_DEF         //open queue for input

        + MQC.MQOO_FAIL_IF_QUIESCING ); //but not if MQM stopping

}
catch( MQException mqe )
{
    //...

}
//Step 3. GET the request message. Note that 

//you decide how long you wait for a message

//to show up. As you are the server now, you 

//do no matching - you must serve to all

//clients. Guess why this method needed a 

//separate thread? Because the GET() can hang the

//thread if it is long enough. Note that 

//request queue is NOT closed after GET is over.

try
{
    requestMessage = new MQMessage();
    MQGetMessageOptions gmo = new MQGetMessageOptions();
    gmo.Options = MQC.MQGMO_WAIT;          // must be specified if 

                                           // you use wait interval

    if( nud_GRPBR_WaitMS.Enabled == false )
        gmo.WaitInterval = MQC.MQWI_UNLIMITED; // wait forever

    else
        gmo.WaitInterval = ( int ) nud_GRPBR_WaitMS.Value;
    gmo.MatchOptions = MQC.MQMO_NONE;       // no matching required

    requestQueue.Get(requestMessage, gmo);
    tb_GRPBR_REQUEST_TEXT.Text = 
      requestMessage.ReadString(requestMessage.MessageLength);
}
catch( MQException mqe )
{
    //...

}
//Step 4. Open Response Queue to write the 

//reply message. Note that we retrieve the

//"reply to" queue name and manager name 

//from the request message itself. That's why

//we do not need the Response Queue name 

//from the user in our GUI in this case.

try
{
    responseQueue = 
       mqQMgr.AccessQueue(requestMessage.ReplyToQueueName, 
       MQC.MQOO_OUTPUT, requestMessage.ReplyToQueueManagerName, 
       null, null);
}
catch( MQException mqe )
{
    //...

}
//Step 5. Reply back (PUT the response 

//message) Note the response "matching"

//mechanism to ensure that when the 

//sender receives the correct ID in the

//correct place (as requested by sender), 

//so that it can "match" the response.


//Step 6. Close Request, Response Queues and Disconnect Manager.

try
{
    responseMessage = new MQMessage();
    MQPutMessageOptions pmo = new MQPutMessageOptions();
    pmo.Options = MQC.MQPMO_NONE;

    if ((requestMessage.Report & MQC.MQRO_PASS_MSG_ID) == 
                                            MQC.MQRO_PASS_MSG_ID)
        responseMessage.MessageId = requestMessage.CorrelationId;
    else                        // Assume MQRO_NEW_MSG_ID

        pmo.Options = MQC.MQPMO_NEW_MSG_ID;

    if ((requestMessage.Report & MQC.MQRO_PASS_CORREL_ID) == 
                                           MQC.MQRO_PASS_CORREL_ID)
      responseMessage.CorrelationId = requestMessage.CorrelationId;
    else                   // Assume MQRO_COPY_MSG_ID_TO_CORREL_ID

        responseMessage.CorrelationId = requestMessage.MessageId;

    responseMessage.MessageType = MQC.MQMT_REPLY;
    responseMessage.WriteString( strResponseText );
    responseQueue.Put(responseMessage);
    if( responseQueue.OpenStatus )
        responseQueue.Close();
    if( requestQueue.OpenStatus )
        requestQueue.Close();
    if( mqQMgr.ConnectionStatus )
        mqQMgr.Disconnect();
    btn_GRPBR_ABORTWAIT.Enabled = false;
    return;
}
catch( MQException mqe )
{
    //...

}

Points of interest

This sample is definitely not thread safe. Imagine a client program that has multiple threads in it. If the threads PUT their requests and try to GUT the response from the same queue at the same time, then you have to do time slicing on your GETs. Calling a GET with a wait time of 10 seconds (worse infinite) will not work - say the server replied to one of the requests, but did not reply to the other. But the thread for which there was no reply is waiting with a GET with a wait time of say 30 seconds. It will wait for 30 seconds, while the other thread will do nothing, but had the other thread called the GET and not this one, it would have got the reply immediately, as it was lying there. Obviously, this assumes that you have called lock around your GET call. So calling GET with a big wait time can degrade the performance in a multi threaded environment. In that case you have to write more manipulative code - and that is called "time slicing" (Actually I call it time slicing, I don't know whether that is an iconized way of calling it).

I am currently writing a C# DLL that provides such time slicing functionalities. Any help is welcome!

History

  • V 1.0 - A simple sample. The targeted V 1.1 is a time slicer!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here