ASG
IBM
Zystems
Cressida
Icon
Netflexity
 
  MQSeries.net
Search  Search       Tech Exchange      Education      Certifications      Library      Info Center      SupportPacs      LinkedIn  Search  Search                                                                   FAQ  FAQ   Usergroups  Usergroups
 
Register  ::  Log in Log in to check your private messages
 
RSS Feed - WebSphere MQ Support RSS Feed - Message Broker Support

MQSeries.net Forum IndexUser ExitsC++, Publish exit: cannot perform MQPUT, MQPUT1 not works

Post new topicReply to topic
C++, Publish exit: cannot perform MQPUT, MQPUT1 not works View previous topic :: View next topic
Author Message
xeonix
PostPosted: Thu Nov 21, 2013 2:09 am Post subject: C++, Publish exit: cannot perform MQPUT, MQPUT1 not works Reply with quote

Apprentice

Joined: 02 Apr 2013
Posts: 32

Greetings gentlemen!

For my task I need to create "Publish Exit" which would act as event handler (or like database trigger) when publication to specific topic occurs. When "Publish" event occurs, my "Publish Exit" should redistribute message being published to one or more other topics, using certain rules.

I created my "Publish Exit" using Microsoft Visual C++ 2012, and as an example I took 'C:\IBM\WebSphere MQ\tools\c\Samples\amqspse0.c' - sample publish exit which demonstrates how to trace "Publish Exit" event to file.
I'm linking to 'mqm.Lib'.
So I built DLL, registered it in "qm.ini", restarted QMgr placed some breakpoints and attached debugger to following processes:

amqzlsa0.exe
amqfqpub.exe
amqzxma0.exe

A-ha! It works, and break-point is hit when I'm making test publication to the target topic using MQ Explorer.
Here's my code (the most important it's part):

Code:

#include <windows.h>
#include <atlbase.h>

#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#include <cmqec.h>

static void Initialize(PMQPSXP pExitParms) { pExitParms->ExitResponse = MQXCC_OK; }

static void Publish(PMQPSXP pExitParms, PMQPBC pPubContext, PMQSBC pSubContext) {
   MQINT32 completionCode, resonCode, topicStringLength = MQ_TOPIC_STR_LENGTH;             
   char topicString[MQ_TOPIC_STR_LENGTH];
   memset(topicString, '\0', MQ_TOPIC_STR_LENGTH);

   pExitParms->pEntryPoints->MQXCNVC_Call(
      pExitParms->Hconn, MQDCC_DEFAULT_CONVERSION + MQDCC_SOURCE_ENC_NATIVE + MQDCC_TARGET_ENC_NATIVE,
      pPubContext->PubTopicString.VSCCSID,
      pPubContext->PubTopicString.VSLength,
      (PMQCHAR)pPubContext->PubTopicString.VSPtr,
      COMPILER_CCSID, topicStringLength, topicString, &topicStringLength, &completionCode, &resonCode);

   if (strcmp("<SOME_TOPIC_STRING>", topicString) == 0) {
      MQLONG completionCode, resonCode;
      char *topicStringPath = "Log";
      MQOD topicDescriptor = { MQOD_DEFAULT };
      strncpy_s(topicDescriptor.ObjectName, "<SOME_TOPIC_TO_DISTRIBUTE_TO>", MQ_TOPIC_NAME_LENGTH);
      topicDescriptor.ObjectString.VSPtr = topicStringPath;
      topicDescriptor.ObjectString.VSLength = strlen(topicStringPath);
      topicDescriptor.ObjectType = MQOT_TOPIC;
      topicDescriptor.ObjectType = MQOT_Q;
      topicDescriptor.Version = MQOD_VERSION_4;
            
      MQMD messageDescriptor = { MQMD_DEFAULT };
      MQPMO putMessageOptions = { MQPMO_DEFAULT };

      char *data = "hello world!";
      MQPUT1(pExitParms->Hconn, &topicDescriptor, &messageDescriptor, &putMessageOptions,
         strlen(data), data,
         &completionCode, &resonCode);
   }
   pExitParms->ExitResponse = MQXCC_OK;
}

static void Terminate(PMQPSXP pExitParms) {  pExitParms->ExitResponse = MQXCC_OK; }

void MQENTRY MQStart() { }

MQ_PUBLISH_EXIT EntryPoint;
void MQENTRY EntryPoint(PMQPSXP pExitParms, PMQPBC pPubContext, PMQSBC pSubContext) {
   switch (pExitParms->ExitReason) {
      case MQXR_INIT:
         Initialize(pExitParms);
         break;
      case MQXR_PUBLICATION:
         Publish(pExitParms, pPubContext, pSubContext);
         break;
      case MQXR_TERM:
         Terminate(pExitParms);
         break;
      default:
         pExitParms->ExitResponse = MQXCC_FAILED;
   }
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; }


For topic "<SOME_TOPIC_TO_DISTRIBUTE_TO>" I created subscription, which puts every message, published in the topic to local queue, say "TESTQ1".
The problem is, that "MQPUT1" returns '0' for both 'completionCode' and 'resonCode', BUT message doesn't appear "TESTQ1".

I also create Win32 console application, and almost the same code works:

Code:

   MQHCONN connectionHandle;

   MQLONG completionCode, resonCode;
   char *topicStringPath = "Log";
   MQOD topicDescriptor = { MQOD_DEFAULT };
   strncpy_s(topicDescriptor.ObjectName, "<SOME_TOPIC_TO_DISTRIBUTE_TO>", MQ_TOPIC_NAME_LENGTH);
   topicDescriptor.ObjectString.VSPtr = topicStringPath;
   topicDescriptor.ObjectString.VSLength = strlen(topicStringPath);
   topicDescriptor.ObjectType = MQOT_TOPIC;
   topicDescriptor.ObjectType = MQOT_Q;
   topicDescriptor.Version = MQOD_VERSION_4;
         
   MQMD messageDescriptor = { MQMD_DEFAULT };
   MQPMO putMessageOptions = { MQPMO_DEFAULT };

   char *data = "hello world!";

   MQCONN("<MY_QMGR>", &connectionHandle, &completionCode, &resonCode);

   MQPUT1(connectionHandle, &topicDescriptor, &messageDescriptor, &putMessageOptions,
      strlen(data), data,
      &completionCode, &resonCode);


I also tried to put messages in my "Publish Exit", using "MQOPEN->MQPUT->MQCLOSE":

Code:

   MQOPEN(pExitParms->Hconn, &topicDescriptor, MQOO_OUTPUT | MQOO_FAIL_IF_QUIESCING, &topicHandle, &completionCode, &resonCode);
   if (completionCode == MQCC_OK) {
      MQPUT(pExitParms->Hconn, topicHandle, &messageDescriptor, &putMessageOptions,
         strlen(data), data,
         &completionCode, &resonCode);
      MQCLOSE(pExitParms->Hconn, &topicHandle, MQCO_NONE, &completionCode, &resonCode);
   }


But in MQOPEN got:
completionCode = 2
resonCode = 2012

I even tried to create another connection using "MQCONN" and to invoke "MQOPEN" using newly create connection handle, instead of "Publish Exit" provided (pExitParms->Hconn), but got same "resonCode = 2012" at "MQOPEN" call.

So, please, can anybody explain to me, is it possible to make MQPUT/MQPU1 calls from "Publish Exit" at all?
Or "Publish Exit" can only modify and/or discard message being published to the topic?

MQ version is 7.5.0.2, OS: Windows 2008 R2
Back to top
View user's profile Send private message
PaulClarke
PostPosted: Thu Nov 21, 2013 3:45 am Post subject: Reply with quote

Guardian

Joined: 17 Nov 2005
Posts: 943
Location: New Zealand

To be honest I've never written a Publish Exit but the manual does seem to be fairly clear. It says

Quote:
The publish exit can do the following operations:

- Examine the contents of the message published to each subscriber
- Modify the contents of the message published to each subscriber
- Alter the queue to which the message is put
- Choose not to deliver the message to the subscriber


No mention there of being able to issue further MQI calls. It wouldn't surprise me if this was a restriction. After all you are in the middle of another MQI call at the time.

Is it possible you could do what you want to do in an API crossing exit ? In those you can issue further MQI calls.

Another alternative, although not pleasant, it to do some form of context switch in your Publish exit and issue the MQI calls you want on a different thread/process.

Cheers,
Paul.
_________________
Paul Clarke
MQGem Software
www.mqgem.com
Back to top
View user's profile Send private message Visit poster's website
xeonix
PostPosted: Thu Nov 21, 2013 4:46 am Post subject: Reply with quote

Apprentice

Joined: 02 Apr 2013
Posts: 32

Found it:

Code:
A publish exit can use some MQI calls. It can only use those MQI calls that manipulate message properties. The calls are:
    MQBUFMH
    MQCRTMH
    MQDLTMH
    MQDLTMP
    MQMHBUF
    MQINQMP
    MQSETMP


Although Publish Exit was the most obvious solution for intercepting TOPIC publications, I think I can make use of standard API exit, taking "WebSphere MQ\tools\c\Samples\amqsaem0.c" as an example.
In this case "PutBefore/Put1Before and/or PutAfter/Put1After" handlers would be invoked for every MQPUT/MQPUT1 call, including those that targets queues (while I'm interested in topics only). So I have to make additional filtering. And also, this solution would affect performance much more than Publish Exit, because Publish Exits are invoked for topic publications only. Am I correct?

Can you tell me, how do I switch context in my Publish Exit?
Invoking process is not an option, so I need to create separate thread in "Publish" handler and perform MQI calls there? Could that help?

Regards,
Alexey
Back to top
View user's profile Send private message
PaulClarke
PostPosted: Thu Nov 21, 2013 5:12 am Post subject: Reply with quote

Guardian

Joined: 17 Nov 2005
Posts: 943
Location: New Zealand

First let me say that I can't guarantee that switching context will work although I can't see any reason why it wouldn't. The 'problem' you see is that MQ maintains a lot of state associated with the current thread. If you were to 'pause' the current thread, issue an MQI call somewhere else and then return with the answer it should all work.

However, I suspect that you may indeed have to switch context to a different process. I believe the Publish exit is invoked from the MQ agent process and I suspect there's a good chance that an MQCONN(X) won't work in that process - I'm guessing you'll get MQRC_ENVIRONMENT_ERROR. So, it may be that you have to do a process switch. Not hard to do but a bit of a pain and very hard to do in a portable fashion.

Regards,
Paul.
_________________
Paul Clarke
MQGem Software
www.mqgem.com
Back to top
View user's profile Send private message Visit poster's website
mqjeff
PostPosted: Thu Nov 21, 2013 5:14 am Post subject: Reply with quote

Grand Master

Joined: 25 Jun 2008
Posts: 17447

It would probably be better to use a wildcard subscription that matched all published messages, and republished the right ones.

Or since you really seem to just want a logging feature, to log all published messages.

That is, write a separate application that acts as a subscriber, not try to handle this at the exit level.
Back to top
View user's profile Send private message
xeonix
PostPosted: Thu Nov 21, 2013 5:31 am Post subject: Reply with quote

Apprentice

Joined: 02 Apr 2013
Posts: 32

PaulClarke wrote:
First let me say that I can't guarantee that switching context will work although I can't see any reason why it wouldn't. The 'problem' you see is that MQ maintains a lot of state associated with the current thread. If you were to 'pause' the current thread, issue an MQI call somewhere else and then return with the answer it should all work.

However, I suspect that you may indeed have to switch context to a different process. I believe the Publish exit is invoked from the MQ agent process and I suspect there's a good chance that an MQCONN(X) won't work in that process - I'm guessing you'll get MQRC_ENVIRONMENT_ERROR. So, it may be that you have to do a process switch. Not hard to do but a bit of a pain and very hard to do in a portable fashion.

Regards,
Paul.


Thanks, I'll give it a try with threads. If that wouldn't work - then the only good option for me is to create API exit.

mqjeff wrote:
It would probably be better to use a wildcard subscription that matched all published messages, and republished the right ones.

Or since you really seem to just want a logging feature, to log all published messages.

That is, write a separate application that acts as a subscriber, not try to handle this at the exit level.


My task is not that easy. To be more specific, I need to look inside message, analyze its properties/contents, and route to different topics based on certain rules.
Back to top
View user's profile Send private message
mqjeff
PostPosted: Thu Nov 21, 2013 6:20 am Post subject: Reply with quote

Grand Master

Joined: 25 Jun 2008
Posts: 17447

xeonix wrote:
My task is not that easy. To be more specific, I need to look inside message, analyze its properties/contents, and route to different topics based on certain rules.


So you need to purchase IIB, then.
Back to top
View user's profile Send private message
Vitor
PostPosted: Thu Nov 21, 2013 6:40 am Post subject: Reply with quote

Grand High Poobah

Joined: 11 Nov 2005
Posts: 25815
Location: Ohio, USA

mqjeff wrote:
xeonix wrote:
My task is not that easy. To be more specific, I need to look inside message, analyze its properties/contents, and route to different topics based on certain rules.


So you need to purchase IIB, then.




And before you say "there's no budget for that", look at the TCO for your exit against one of the more shaved down license models IIBv9 offers.

(Whilst I am an ex-IBM employee, I do not currently work for IBM, have at no time worked for the sales or marketing functions, accept no liability express or implied for loss or damage howsoever caused and the giant chickens are going to kill us all)
_________________
Honesty is the best policy.
Insanity is the best defence.
Back to top
View user's profile Send private message
xeonix
PostPosted: Thu Nov 28, 2013 5:09 am Post subject: Reply with quote

Apprentice

Joined: 02 Apr 2013
Posts: 32

PaulClarke wrote:
First let me say that I can't guarantee that switching context will work although I can't see any reason why it wouldn't. The 'problem' you see is that MQ maintains a lot of state associated with the current thread. If you were to 'pause' the current thread, issue an MQI call somewhere else and then return with the answer it should all work.

However, I suspect that you may indeed have to switch context to a different process. I believe the Publish exit is invoked from the MQ agent process and I suspect there's a good chance that an MQCONN(X) won't work in that process - I'm guessing you'll get MQRC_ENVIRONMENT_ERROR. So, it may be that you have to do a process switch. Not hard to do but a bit of a pain and very hard to do in a portable fashion.

Regards,
Paul.


Cheers!
It's been a while since I last time continued to develop that Publish Exit.
Ta-da, it works now!
All you need to perform MQPUT in the Publish Exit - is to make that call in the separate thread, just like you said.

Code:

void testMQPUT() {
   MQLONG completionCode, resonCode;
   MQHCONN connectionHandle;

   MQCONN("<MY_QMgr>", &connectionHandle, &completionCode, &resonCode);
   MQHOBJ queueHandle;
   
   MQOD queueDescriptor = { MQOD_DEFAULT };
   strncpy_s(queueDescriptor.ObjectName, "TESTQ", MQ_Q_NAME_LENGTH);
   queueDescriptor.ObjectType = MQOT_Q;
   queueDescriptor.Version = MQOD_VERSION_4;

   MQMD messageDescriptor = { MQMD_DEFAULT };
   MQPMO putMessageOptions = { MQPMO_DEFAULT };

   MQOPEN(connectionHandle, &queueDescriptor, MQOO_OUTPUT | MQOO_FAIL_IF_QUIESCING, &queueHandle, &completionCode, &resonCode);

   char *data = "hello world!";

   MQPUT(connectionHandle, queueHandle, &messageDescriptor, &putMessageOptions, strlen(data), data, &completionCode, &resonCode);
}

static void Publish(PMQPSXP pExitParms, PMQPBC pPubContext, PMQSBC pSubContext) {
    std::thread t(testMQPUT);
    t.join();
   pExitParms->ExitResponse = MQXCC_OK;
}


It works!
Thank you!
Back to top
View user's profile Send private message
gbaddeley
PostPosted: Thu Nov 28, 2013 2:16 pm Post subject: Reply with quote

Padawan

Joined: 25 Mar 2003
Posts: 1998
Location: Melbourne, Australia

If you are only going to put one message per call to the exit, use MQPUT1. It wraps up MQOPEN / MQPUT / MQCLOST into one MQI call.
_________________
Glenn
Back to top
View user's profile Send private message
xeonix
PostPosted: Mon Dec 02, 2013 12:30 am Post subject: Reply with quote

Apprentice

Joined: 02 Apr 2013
Posts: 32

gbaddeley wrote:
If you are only going to put one message per call to the exit, use MQPUT1. It wraps up MQOPEN / MQPUT / MQCLOST into one MQI call.

You're right. But the reason I coded function that way is because previously, when I used MQPUT1 - it's completion and reason codes were "0" while nothing was delivered to the destination queue. And only MQCONN > MQOPEN > MQPUT > MQCLOSE > MQDISC was reporting me an error at "MQOPEN" stage. So, in order to avoid such behavior I prefer not to use "MQPUT1".
Back to top
View user's profile Send private message
gbaddeley
PostPosted: Mon Dec 02, 2013 2:28 pm Post subject: Reply with quote

Padawan

Joined: 25 Mar 2003
Posts: 1998
Location: Melbourne, Australia

xeonix wrote:
But the reason I coded function that way is because previously, when I used MQPUT1 - it's completion and reason codes were "0" while nothing was delivered to the destination queue. And only MQCONN > MQOPEN > MQPUT > MQCLOSE > MQDISC was reporting me an error at "MQOPEN" stage. So, in order to avoid such behavior I prefer not to use "MQPUT1".

MQPUT1 does not behave like this. Maybe you made a coding error? Can you reproduce the problem in a normal program (rather than an exit) ?
_________________
Glenn
Back to top
View user's profile Send private message
Display posts from previous:
Post new topicReply to topic Page 1 of 1

MQSeries.net Forum IndexUser ExitsC++, Publish exit: cannot perform MQPUT, MQPUT1 not works
Jump to:



You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Protected by Anti-Spam ACP


Theme by Dustin Baccetti
Powered by phpBB 2001, 2002 phpBB Group

Copyright MQSeries.net. All rights reserved.