Author |
Message
|
heyandy |
Posted: Tue May 10, 2005 1:58 pm Post subject: Custom WBIMB Node question |
|
|
Newbie
Joined: 23 Jun 2004 Posts: 4
|
I am writing my first custom node for WBIMB V5 and am close, but missing something... Basically the node is going to compress or uncompress a BLOB and put the result back into the output message BLOB field. (Have to do a custom node since the compression we use is a 3rd party tool we bought sometime ago and runs on our mainframe.)
I have the node up and running except when I dump the output message to a queue no data is passed. I create a new message from the context of the incoming message (which was a BLOB), I do NOT copy the incomming BLOB element, but everything else (i.e. Properties and MQMD). On the new output message I create a BLOB element of type CCI_ELEMENT_TYPE_NAME, and then create a child element under that element named BLOB of type CCI_ELEMENT_TYPE_NAME_VALUE. I then added the uncompress (or compressed) data using the cniSetElementByteArrayValue function to that node.
If I put a trace node right after my node and dump ${Root} I see my BLOB.BLOB data and it looks good.. However when I send that through an MQOutput node the data length is 0 and obviously no data is in the queue. I suspect I am missing something about the parser, but not sure...
Can someone help in telling me what I am missing?
Thanks,
Andy |
|
Back to top |
|
 |
malammik |
Posted: Tue May 10, 2005 3:06 pm Post subject: |
|
|
 Partisan
Joined: 27 Jan 2005 Posts: 397 Location: Philadelphia, PA
|
You are obviously using C API but I wanted to make sure you know about the following bug which I uncovered in Java api, it is quite possible that it is in c api as well. API libraries fail to convert an element to bit stream if the element does not have the Root as its parent. For example:
SomeElement se = Root.getChild().getChild();
se.toBitStream() returns null. However Root.toBitSteam() does return a valid byte array. After writing custom nodes I usually spend 30 minutes just placing debug statements everywhere including those for printing pointer address values. I suggest you do the same. Good Luck. _________________ Mikhail Malamud
http://www.netflexity.com
http://groups.google.com/group/qflex |
|
Back to top |
|
 |
jefflowrey |
Posted: Tue May 10, 2005 3:55 pm Post subject: |
|
|
Grand Poobah
Joined: 16 Oct 2002 Posts: 19981
|
I'm not positive you've encountered a bug, malammik - unless IBM has confirmed it.
I'd be more suspicious that the problem with heyandy's code is that the Output message doesn't actually have the BLOB parser attached.
You say you aren't copying "the incoming BLOB element" - is that InputRoot.BLOB, or InputRoot.BLOB.BLOB that you aren't copying? If it's the former, then I suspect that is the problem.
If you're creating OutputRoot.BLOB, then you need to do something to attach the BLOB parser to it - and I only know how to do that using ESQL.
If it is the later... then... ummm...
Does the file trace of your output message have exactly the same numbers for the types of the elements as a "proper" blob message?
You might also consider having your node populate fields in Environment or LocalEnvironment, and use ESQL to put them in the right places in the Output tree. _________________ I am *not* the model of the modern major general. |
|
Back to top |
|
 |
fschofer |
Posted: Wed May 11, 2005 12:03 am Post subject: |
|
|
 Knight
Joined: 02 Jul 2001 Posts: 524 Location: Mainz, Germany
|
Hi,
i think the unzip plugin does a similar thing.
Maybe it helps to take a look at its source code
http://www-1.ibm.com/support/docview.wss?rs=171&uid=swg24007617&loc=en_US&cs=utf-8&lang=en
Quote: |
/* Get the root element of the output message */
outRootElement = cniRootElement(&rc, outMsg);
checkRC(rc);
/* Copy the contents of the input message to the output message */
if (BlobCopy == FALSE)
cniCopyElementTreeWithoutBLOB(&rc, rootElement, outRootElement);
else
cniCopyElementTree(&rc, rootElement, outRootElement);
checkRC(rc);
bodyChild = cniCreateElementAsLastChild(&rc, outRootElement);
checkRC(rc);
cniSetElementType(&rc, bodyChild, CCI_ELEMENT_TYPE_NAME);
checkRC(rc);
cniSetElementName(&rc, bodyChild, (CciChar *)constUNZIP);
checkRC(rc);
//Create Blob Structure
Blob = (struct CciByteArray *)malloc(sizeof(struct CciByteArray));
Blob->pointer = (char *)malloc(sizeof(char) * 10240);
Blob->size = 10240;
blobSize = cniElementByteArrayValue(&rc, blobElement, Blob);
#ifdef _DEBUG
fprintf(outputFile, "%d = cniElementByteArrayValue rc = %d\n", blobSize, rc);
fflush(outputFile);
#endif
if (rc == CCI_BUFFER_TOO_SMALL) { |
Greetings
Frank |
|
Back to top |
|
 |
heyandy |
Posted: Wed May 11, 2005 4:55 am Post subject: Further information... |
|
|
Newbie
Joined: 23 Jun 2004 Posts: 4
|
Yes I am using the C API (trust me would prefer to use Java, but Java is a 4 letter word around here)...
Anyway, to Jeff's question, I am not copying anything under InputRoot.BLOB... And I was curious about that... I have a trace node just before my node that dumps ${Root} and I get this snippet of the tree:
(0x01000000):BLOB = (
(0x03000000):UnknownParserName = ''
(0x03000000):BLOB =
I put a trace node after my node and dump ${Root} and get the following:
(0x01000000):BLOB = (
(0x03000000):BLOB =
So I wonder if it is just as simple as attaching a CCI_ELEMENT_TYPE_NAME_VALUE node under the first BLOB named "UnknownParserName"? Guess I will go give it a shot and let anyone interested in this topic know...
As to the unzip plugin, that is the sample I gutted to do my node.. However that node does not setup the output tree to be directly sent to a MQOutput node, as you can see by the code they add a "UnZip" branch to the OutputRoot tree that you would then have to manipulate in ESQL afterwards. Since the Compress node I am writing will generally be the last thing done before going to a MQOutput node I am trying to avoid developers having to insert a Compute node after my custom node to save time (we run in the 20 to 30 transactions a second on one server, so performance is always critical.)
Andy |
|
Back to top |
|
 |
jefflowrey |
Posted: Wed May 11, 2005 4:59 am Post subject: Re: Further information... |
|
|
Grand Poobah
Joined: 16 Oct 2002 Posts: 19981
|
heyandy wrote: |
So I wonder if it is just as simple as attaching a CCI_ELEMENT_TYPE_NAME_VALUE node under the first BLOB named "UnknownParserName"? Guess I will go give it a shot and let anyone interested in this topic know... |
I'd be surprised if that worked.
Again, I'd try copying InputRoot.BLOB.BLOB, and then replacing it. _________________ I am *not* the model of the modern major general. |
|
Back to top |
|
 |
heyandy |
Posted: Wed May 11, 2005 6:43 am Post subject: Got it working... |
|
|
Newbie
Joined: 23 Jun 2004 Posts: 4
|
My first thought did not work, so I did take Jeff's suggestion which is I just copied the entire InputRoot to the OutputRoot and then just replaced the BLOB.BLOB field with the new data.
My concern with that is now I am copying the entire message (which could be as high as 256K bytes) just to replace it with another data stream. Again this flow will be in a high volume application and any extra time is a concern.
So my question, is there a way to associate the correct parser to a newly created output message short of copying the original message?
Thanks again,
Andy |
|
Back to top |
|
 |
jefflowrey |
Posted: Wed May 11, 2005 7:05 am Post subject: |
|
|
Grand Poobah
Joined: 16 Oct 2002 Posts: 19981
|
|
Back to top |
|
 |
jhosie |
Posted: Thu May 12, 2005 1:15 am Post subject: cniCreateElementAfterUsingParser |
|
|
Apprentice
Joined: 12 May 2005 Posts: 28
|
Assuming that you created the BLOB element using one of the following calls
cniCreateElementAfter
cniCreateElementAsLastChild
I agree with jefflowrey, you should use one of the following functions:
cniCreateElementAsLastChildUsingParser or cniCreateElementAfterUsingParser
http://publib.boulder.ibm.com/infocenter/wbihelp/index.jsp?topic=/com.ibm.etools.mft.doc/as07810_.htm
http://publib.boulder.ibm.com/infocenter/wbihelp/index.jsp?topic=/com.ibm.etools.mft.doc/as07870_.htm
and you should specify "NONE" for the parserClassName
http://publib.boulder.ibm.com/infocenter/wbihelp/index.jsp?topic=/com.ibm.etools.mft.doc/as07410_.htm
The thinking behind this is, when you create an element using e.g. cniCreateElementAsLastChild or cniCreateElementAfter, then that element belongs to the same parser as its parent. Probably the Root parser in this case.
So when the MQOutputNode asks the parsers for their bitstreams, to put onto the queue, he only gets the MQMD bitstream. This is because the rest of the tree, including the BLOB element that you created is owned by either the Properties parser or the Root parser. Neither of which can produce any bitstream.
I am assuming that you created the BLOB element by using one of the following calls
cniCreateElementAfter
cniCreateElementAsLastChild
Most people coming from ESQL make this mistake.
When you do the following in ESQL
SET OutputRoot.BLOB.BLOB = mydata;
ESQL is kind enough to notice that you have created an element, as a direct child of the root element and you named it BLOB so you must have intended for it to be in the BLOB domain, so ESQL assigns it to the BLOB parser. The C plug-in API does not make this choice for you. You need to assign it to the BLOB parser yourself. |
|
Back to top |
|
 |
heyandy |
Posted: Fri May 13, 2005 9:37 am Post subject: Thanks! |
|
|
Newbie
Joined: 23 Jun 2004 Posts: 4
|
That was indeed my problem, since I was not associating a parser to the element no output was being done. I am now using the CniCreateElementAsLastChildUsingParser function and no longer copying the entire input message and everything works as expected...
Thanks again everyone for your help.
Andy |
|
Back to top |
|
 |
|