|
RSS Feed - WebSphere MQ Support
|
RSS Feed - Message Broker Support
|
![Reply to topic](templates/DAJ_Glass/images/lang_english/reply.gif) |
|
C++, Publish exit: cannot perform MQPUT, MQPUT1 not works |
« View previous topic :: View next topic » |
Author |
Message
|
xeonix |
Posted: Thu Nov 21, 2013 2:09 am Post subject: C++, Publish exit: cannot perform MQPUT, MQPUT1 not works |
|
|
![](images/avatars/1819155928528de31dadc0c.jpg) 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
PaulClarke |
Posted: Thu Nov 21, 2013 3:45 am Post subject: |
|
|
![](images/avatars/46729506851b1e76395b14.jpg) Grand Master
Joined: 17 Nov 2005 Posts: 1002 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
xeonix |
Posted: Thu Nov 21, 2013 4:46 am Post subject: |
|
|
![](images/avatars/1819155928528de31dadc0c.jpg) 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
PaulClarke |
Posted: Thu Nov 21, 2013 5:12 am Post subject: |
|
|
![](images/avatars/46729506851b1e76395b14.jpg) Grand Master
Joined: 17 Nov 2005 Posts: 1002 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
mqjeff |
Posted: Thu Nov 21, 2013 5:14 am Post subject: |
|
|
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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
xeonix |
Posted: Thu Nov 21, 2013 5:31 am Post subject: |
|
|
![](images/avatars/1819155928528de31dadc0c.jpg) 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
mqjeff |
Posted: Thu Nov 21, 2013 6:20 am Post subject: |
|
|
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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
Vitor |
Posted: Thu Nov 21, 2013 6:40 am Post subject: |
|
|
![](images/avatars/905340816499abc1cdfb07.jpg) Grand High Poobah
Joined: 11 Nov 2005 Posts: 26093 Location: Texas, 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
xeonix |
Posted: Thu Nov 28, 2013 5:09 am Post subject: |
|
|
![](images/avatars/1819155928528de31dadc0c.jpg) 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
gbaddeley |
Posted: Thu Nov 28, 2013 2:16 pm Post subject: |
|
|
![](images/avatars/gallery/Star Wars/movie_star_wars_yoda.gif) Jedi Knight
Joined: 25 Mar 2003 Posts: 2501 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
xeonix |
Posted: Mon Dec 02, 2013 12:30 am Post subject: |
|
|
![](images/avatars/1819155928528de31dadc0c.jpg) 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
gbaddeley |
Posted: Mon Dec 02, 2013 2:28 pm Post subject: |
|
|
![](images/avatars/gallery/Star Wars/movie_star_wars_yoda.gif) Jedi Knight
Joined: 25 Mar 2003 Posts: 2501 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 |
|
![](templates/DAJ_Glass/images/spacer.gif) |
|
|
![Reply to topic](templates/DAJ_Glass/images/lang_english/reply.gif) |
|
Page 1 of 1 |
|
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
|
|
|
|