Skip to content

Commit 95d7aa3

Browse files
committed
Support for GET of GroupID/Seq/LastMsg - #51
1 parent 5fade19 commit 95d7aa3

File tree

3 files changed

+268
-6
lines changed

3 files changed

+268
-6
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ JMS_IBM_MQMD_MsgId msg.GetJMSMessageID()
163163
JMS_IBM_MQMD_ApplOriginData msg.GetStringProperty("JMS_IBM_MQMD_ApplOriginData")
164164
JMSExpiration msg.GetJMSExpiration()
165165
JMSXAppID msg.GetStringProperty("JMSXAppID") JMSXAppID / PutApplName is set using ConnectionFactory.ApplName
166+
JMSXGroupID msg.GetStringProperty("JMSXGroupID")
167+
JMSXGroupSeq msg.GetIntProperty("JMSXGroupSeq")
168+
JMS_IBM_Last_Msg_In_Group msg.GetBooleanProperty("JMS_IBM_Last_Msg_In_Group")
166169
```
167170

168171

messagegroup_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (c) IBM Corporation 2022
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*/
10+
package main
11+
12+
import (
13+
"testing"
14+
15+
"github.com/ibm-messaging/mq-golang-jms20/mqjms"
16+
"github.com/stretchr/testify/assert"
17+
)
18+
19+
/*
20+
* Test the behaviour of message groups.
21+
*
22+
* JMSXGroupID
23+
* JMSXGroupSeq
24+
* JMS_IBM_Last_Msg_In_Group
25+
*
26+
* https://www.ibm.com/docs/en/ibm-mq/9.2?topic=ordering-grouping-logical-messages
27+
*/
28+
func TestMessageGroup(t *testing.T) {
29+
30+
// Loads CF parameters from connection_info.json and applicationApiKey.json in the Downloads directory
31+
cf, cfErr := mqjms.CreateConnectionFactoryFromDefaultJSONFiles()
32+
assert.Nil(t, cfErr)
33+
34+
// Creates a connection to the queue manager, using defer to close it automatically
35+
// at the end of the function (if it was created successfully)
36+
context, ctxErr := cf.CreateContext()
37+
assert.Nil(t, ctxErr)
38+
if context != nil {
39+
defer context.Close()
40+
}
41+
42+
// Set up objects for send/receive
43+
queue := context.CreateQueue("DEV.QUEUE.1")
44+
consumer, errCons := context.CreateConsumer(queue)
45+
if consumer != nil {
46+
defer consumer.Close()
47+
}
48+
assert.Nil(t, errCons)
49+
50+
// Since we need more work to support the "set" operations (see big comment below)
51+
// lets just do a short test of the "get" behaviour.
52+
53+
txtMsg1 := context.CreateTextMessage()
54+
55+
// Force the population of the MQMD field.
56+
myFormat := "MYFMT"
57+
txtMsg1.SetStringProperty("JMS_IBM_Format", &myFormat)
58+
59+
groupId, err := txtMsg1.GetStringProperty("JMSXGroupID")
60+
assert.Nil(t, err)
61+
assert.Nil(t, groupId)
62+
63+
groupSeq, err := txtMsg1.GetIntProperty("JMSXGroupSeq")
64+
assert.Nil(t, err)
65+
assert.Equal(t, 1, groupSeq)
66+
67+
gotLastMsg, err := txtMsg1.GetBooleanProperty("JMS_IBM_Last_Msg_In_Group")
68+
assert.Equal(t, false, gotLastMsg)
69+
70+
myGroup := "hello"
71+
err = txtMsg1.SetStringProperty("JMSXGroupID", &myGroup)
72+
assert.NotNil(t, err)
73+
assert.Equal(t, "Not yet implemented", err.GetLinkedError().Error())
74+
75+
err = txtMsg1.SetIntProperty("JMSXGroupSeq", 2)
76+
assert.NotNil(t, err)
77+
assert.Equal(t, "Not yet implemented", err.GetLinkedError().Error())
78+
79+
err = txtMsg1.SetBooleanProperty("JMS_IBM_Last_Msg_In_Group", true)
80+
assert.NotNil(t, err)
81+
assert.Equal(t, "Not yet implemented", err.GetLinkedError().Error())
82+
83+
/*
84+
* Setting these properties requires an MQMD V2 header and is also
85+
* not supported for PUT1 operations so there is some more extensive
86+
* implementation work required in order to enable the "set" scenarios
87+
* for these Group properties.
88+
89+
// Create a TextMessage and check that we can populate it
90+
txtMsg1 := context.CreateTextMessage()
91+
txtMsg1.SetText(msgBody)
92+
txtMsg1.SetStringProperty("JMSXGroupID", &groupID)
93+
txtMsg1.SetIntProperty("JMSXGroupSeq", 1)
94+
errSend := producer.Send(queue, txtMsg1)
95+
assert.Nil(t, errSend)
96+
97+
txtMsg2 := context.CreateTextMessage()
98+
txtMsg2.SetText(msgBody)
99+
txtMsg2.SetStringProperty("JMSXGroupID", &groupID)
100+
txtMsg2.SetIntProperty("JMSXGroupSeq", 2)
101+
errSend = producer.Send(queue, txtMsg2)
102+
assert.Nil(t, errSend)
103+
104+
txtMsg3 := context.CreateTextMessage()
105+
txtMsg3.SetText(msgBody)
106+
txtMsg3.SetStringProperty("JMSXGroupID", &groupID)
107+
txtMsg3.SetIntProperty("JMSXGroupSeq", 3)
108+
txtMsg3.SetBooleanProperty("JMS_IBM_Last_Msg_In_Group", true)
109+
errSend = producer.Send(queue, txtMsg3)
110+
assert.Nil(t, errSend)
111+
112+
// Check the first message.
113+
rcvMsg, errRvc := consumer.ReceiveNoWait()
114+
assert.Nil(t, errRvc)
115+
assert.NotNil(t, rcvMsg)
116+
assert.Equal(t, txtMsg1.GetJMSMessageID(), rcvMsg.GetJMSMessageID())
117+
gotGroupIDValue, gotErr := rcvMsg.GetStringProperty("JMSXGroupID")
118+
assert.Nil(t, gotErr)
119+
assert.Equal(t, groupID, *gotGroupIDValue)
120+
gotSeqValue, gotErr := rcvMsg.GetIntProperty("JMSXGroupSeq")
121+
assert.Equal(t, 1, gotSeqValue)
122+
gotLastMsgValue, gotErr := rcvMsg.GetBooleanProperty("JMS_IBM_Last_Msg_In_Group")
123+
assert.Equal(t, false, gotLastMsgValue)
124+
125+
// Check the second message.
126+
rcvMsg, errRvc = consumer.ReceiveNoWait()
127+
assert.Nil(t, errRvc)
128+
assert.NotNil(t, rcvMsg)
129+
assert.Equal(t, txtMsg2.GetJMSMessageID(), rcvMsg.GetJMSMessageID())
130+
gotGroupIDValue, gotErr = rcvMsg.GetStringProperty("JMSXGroupID")
131+
assert.Nil(t, gotErr)
132+
assert.Equal(t, groupID, *gotGroupIDValue)
133+
gotSeqValue, gotErr = rcvMsg.GetIntProperty("JMSXGroupSeq")
134+
assert.Equal(t, 2, gotSeqValue)
135+
gotLastMsgValue, gotErr = rcvMsg.GetBooleanProperty("JMS_IBM_Last_Msg_In_Group")
136+
assert.Equal(t, false, gotLastMsgValue)
137+
138+
// Check the third message.
139+
rcvMsg, errRvc = consumer.ReceiveNoWait()
140+
assert.Nil(t, errRvc)
141+
assert.NotNil(t, rcvMsg)
142+
assert.Equal(t, txtMsg3.GetJMSMessageID(), rcvMsg.GetJMSMessageID())
143+
gotGroupIDValue, gotErr = rcvMsg.GetStringProperty("JMSXGroupID")
144+
assert.Nil(t, gotErr)
145+
assert.Equal(t, groupID, *gotGroupIDValue)
146+
gotSeqValue, gotErr = rcvMsg.GetIntProperty("JMSXGroupSeq")
147+
assert.Equal(t, 3, gotSeqValue)
148+
gotLastMsgValue, gotErr = rcvMsg.GetBooleanProperty("JMS_IBM_Last_Msg_In_Group")
149+
assert.Equal(t, true, gotLastMsgValue)
150+
*/
151+
152+
}

mqjms/MessageImpl.go

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package mqjms
1111

1212
import (
1313
"encoding/hex"
14+
"errors"
1415
"fmt"
1516
"log"
1617
"strconv"
@@ -301,9 +302,13 @@ func (msg *MessageImpl) SetStringProperty(name string, value *string) jms20subse
301302
var linkedErr error
302303

303304
// Different code path and shortcut for special header properties
304-
isSpecial, _ := msg.setSpecialStringPropertyValue(name, value)
305+
isSpecial, specialErr := msg.setSpecialStringPropertyValue(name, value)
305306
if isSpecial {
306-
return nil
307+
308+
if specialErr != nil {
309+
retErr = jms20subset.CreateJMSException("4125", "MQJMS4125", specialErr)
310+
}
311+
return retErr
307312
}
308313

309314
if value != nil {
@@ -367,6 +372,15 @@ func (msg *MessageImpl) setSpecialStringPropertyValue(name string, value *string
367372
msg.mqmd.Format = ibmmq.MQFMT_NONE // unset
368373
}
369374

375+
case "JMSXGroupID":
376+
err = errors.New("Not yet implemented")
377+
/* Implementation not yet complete
378+
if value != nil {
379+
groupBytes := convertStringToMQBytes(*value)
380+
msg.mqmd.GroupId = groupBytes
381+
msg.mqmd.MsgFlags |= ibmmq.MQMF_MSG_IN_GROUP
382+
} */
383+
370384
default:
371385
isSpecial = false
372386
}
@@ -413,6 +427,10 @@ func (msg *MessageImpl) setSpecialIntPropertyValue(name string, value int) (bool
413427
case "JMS_IBM_MQMD_MsgType":
414428
msg.mqmd.MsgType = int32(value)
415429

430+
case "JMSXGroupSeq":
431+
err = errors.New("Not yet implemented")
432+
//msg.mqmd.MsgSeqNumber = int32(value)
433+
416434
default:
417435
isSpecial = false
418436
}
@@ -498,6 +516,38 @@ func (msg *MessageImpl) getSpecialPropertyValue(name string) (bool, interface{},
498516
value = msg.mqmd.MsgType
499517
}
500518

519+
case "JMSXGroupID":
520+
if msg.mqmd != nil {
521+
valueBytes := msg.mqmd.GroupId
522+
523+
// See whether this is a non-zero response.
524+
nonZeros := false
525+
for _, thisByte := range valueBytes {
526+
if thisByte != 0 {
527+
nonZeros = true
528+
break
529+
}
530+
}
531+
532+
if nonZeros {
533+
value = hex.EncodeToString(valueBytes)
534+
}
535+
}
536+
537+
case "JMSXGroupSeq":
538+
if msg.mqmd != nil {
539+
value = msg.mqmd.MsgSeqNumber
540+
} else {
541+
value = int32(1)
542+
}
543+
544+
case "JMS_IBM_Last_Msg_In_Group":
545+
if msg.mqmd != nil {
546+
value = ((msg.mqmd.MsgFlags & ibmmq.MQMF_LAST_MSG_IN_GROUP) != 0)
547+
} else {
548+
value = false
549+
}
550+
501551
default:
502552
isSpecial = false
503553
}
@@ -579,9 +629,13 @@ func (msg *MessageImpl) SetIntProperty(name string, value int) jms20subset.JMSEx
579629
var linkedErr error
580630

581631
// Different code path and shortcut for special header properties
582-
isSpecial, _ := msg.setSpecialIntPropertyValue(name, value)
632+
isSpecial, specialErr := msg.setSpecialIntPropertyValue(name, value)
583633
if isSpecial {
584-
return nil
634+
635+
if specialErr != nil {
636+
retErr = jms20subset.CreateJMSException("4125", "MQJMS4125", specialErr)
637+
}
638+
return retErr
585639
}
586640

587641
smpo := ibmmq.NewMQSMPO()
@@ -696,7 +750,13 @@ func (msg *MessageImpl) GetDoubleProperty(name string) (float64, jms20subset.JMS
696750
impo := ibmmq.NewMQIMPO()
697751
pd := ibmmq.NewMQPD()
698752

699-
_, value, err := msg.msgHandle.InqMP(impo, pd, name)
753+
// Check first if this is a special property
754+
isSpecialProp, value, err := msg.getSpecialPropertyValue(name)
755+
756+
if !isSpecialProp {
757+
// If not then look for a user property
758+
_, value, err = msg.msgHandle.InqMP(impo, pd, name)
759+
}
700760

701761
if err == nil {
702762

@@ -748,6 +808,16 @@ func (msg *MessageImpl) SetBooleanProperty(name string, value bool) jms20subset.
748808
smpo := ibmmq.NewMQSMPO()
749809
pd := ibmmq.NewMQPD()
750810

811+
// Different code path and shortcut for special header properties
812+
isSpecial, specialErr := msg.setSpecialBooleanPropertyValue(name, value)
813+
if isSpecial {
814+
815+
if specialErr != nil {
816+
retErr = jms20subset.CreateJMSException("4125", "MQJMS4125", specialErr)
817+
}
818+
return retErr
819+
}
820+
751821
linkedErr = msg.msgHandle.SetMP(smpo, name, pd, value)
752822

753823
if linkedErr != nil {
@@ -760,6 +830,37 @@ func (msg *MessageImpl) SetBooleanProperty(name string, value bool) jms20subset.
760830
return retErr
761831
}
762832

833+
// setSpecialBooleanPropertyValue sets the special header properties of type bool
834+
func (msg *MessageImpl) setSpecialBooleanPropertyValue(name string, value bool) (bool, error) {
835+
836+
// Special properties always start with a known prefix.
837+
if !strings.HasPrefix(name, "JMS") {
838+
return false, nil
839+
}
840+
841+
// Check first that there is an MQMD to write to
842+
if msg.mqmd == nil {
843+
msg.mqmd = ibmmq.NewMQMD()
844+
}
845+
846+
// Assume for now that this property is special as it has passed the basic
847+
// checks, and this value will be set back to false if it doesn't match any
848+
// of the specific fields.
849+
isSpecial := true
850+
851+
var err error
852+
853+
switch name {
854+
case "JMS_IBM_Last_Msg_In_Group":
855+
err = errors.New("Not yet implemented")
856+
857+
default:
858+
isSpecial = false
859+
}
860+
861+
return isSpecial, err
862+
}
863+
763864
// GetBooleanProperty returns the bool value of a named message property.
764865
// Returns false if the named property is not set.
765866
func (msg *MessageImpl) GetBooleanProperty(name string) (bool, jms20subset.JMSException) {
@@ -770,7 +871,13 @@ func (msg *MessageImpl) GetBooleanProperty(name string) (bool, jms20subset.JMSEx
770871
impo := ibmmq.NewMQIMPO()
771872
pd := ibmmq.NewMQPD()
772873

773-
_, value, err := msg.msgHandle.InqMP(impo, pd, name)
874+
// Check first if this is a special property
875+
isSpecialProp, value, err := msg.getSpecialPropertyValue(name)
876+
877+
if !isSpecialProp {
878+
// If not then look for a user property
879+
_, value, err = msg.msgHandle.InqMP(impo, pd, name)
880+
}
774881

775882
if err == nil {
776883

0 commit comments

Comments
 (0)